@did-btcr2/method 0.36.1 → 0.38.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 +1149 -126
- package/dist/browser.mjs +1149 -126
- package/dist/cjs/index.js +1244 -208
- 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 +210 -21
- 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 +12 -3
- package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js +74 -22
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +104 -11
- 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 +116 -20
- 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/fee-estimator.js +8 -0
- package/dist/esm/core/beacon/fee-estimator.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 +41 -7
- 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 +19 -3
- package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/events.d.ts +28 -7
- package/dist/types/core/aggregation/runner/events.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/participant-runner.d.ts +3 -3
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts +47 -5
- 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 +66 -10
- 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/fee-estimator.d.ts +8 -0
- package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -1
- 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 +255 -23
- 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 +34 -5
- package/src/core/aggregation/runner/events.ts +26 -7
- package/src/core/aggregation/runner/participant-runner.ts +75 -23
- package/src/core/aggregation/runner/service-runner.ts +144 -12
- 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 +149 -25
- package/src/core/beacon/cas-beacon.ts +7 -7
- package/src/core/beacon/fee-estimator.ts +9 -0
- 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
|
@@ -7,37 +7,34 @@ import { Address, OutScript, p2pkh, p2tr, p2wpkh, Script, SigHash, Transaction }
|
|
|
7
7
|
import type { BeaconProcessResult } from '../resolver.js';
|
|
8
8
|
import type { SidecarData } from '../types.js';
|
|
9
9
|
import { BeaconError } from './error.js';
|
|
10
|
-
import {
|
|
10
|
+
import { DEFAULT_FEE_ESTIMATOR } from './fee-estimator.js';
|
|
11
11
|
import type { FeeEstimator } from './fee-estimator.js';
|
|
12
12
|
import type { BeaconService, BeaconSignal } from './interfaces.js';
|
|
13
13
|
|
|
14
|
-
/** Default fee estimator used when none is supplied. ~5 sat/vB static rate. */
|
|
15
|
-
const DEFAULT_FEE_ESTIMATOR: FeeEstimator = new StaticFeeEstimator(5);
|
|
16
|
-
|
|
17
14
|
/**
|
|
18
15
|
* Singleton beacon script kinds. Per the did:btcr2 spec, deterministic DID documents
|
|
19
|
-
* include three beacon services: P2PKH, P2WPKH, and P2TR (taproot key-path)
|
|
16
|
+
* include three beacon services: P2PKH, P2WPKH, and P2TR (taproot key-path), all
|
|
20
17
|
* derived from the genesis secp256k1 public key. The singleton broadcast path must
|
|
21
18
|
* support signing for all three.
|
|
22
19
|
*/
|
|
23
20
|
export type SingletonScriptKind = 'p2pkh' | 'p2wpkh' | 'p2tr';
|
|
24
21
|
|
|
25
22
|
/**
|
|
26
|
-
* Conservative vsize estimate for a 1-input P2TR key-path
|
|
23
|
+
* Conservative vsize estimate for a 1-input P2TR key-path to 1 P2TR change + 1 OP_RETURN(32) tx.
|
|
27
24
|
* Stripped 137 + witness ≈ 68 (marker + flag + stack-count + sig-len + 64 BIP-340 sig).
|
|
28
25
|
* Weight = 137*4 + 68 = 616, vsize ≈ 154, rounded to 160 for headroom.
|
|
29
26
|
*/
|
|
30
27
|
export const P2TR_BEACON_TX_VSIZE = 160;
|
|
31
28
|
|
|
32
29
|
/**
|
|
33
|
-
* Conservative vsize estimate for a 1-input P2WPKH
|
|
30
|
+
* Conservative vsize estimate for a 1-input P2WPKH to 1 P2WPKH change + 1 OP_RETURN(32) tx.
|
|
34
31
|
* Stripped 125 + witness ≈ 110 (worst-case DER ECDSA sig 72 + sighash byte + 33 pubkey + framing).
|
|
35
32
|
* vsize = ceil((125*4 + 110) / 4) ≈ 153, rounded to 155.
|
|
36
33
|
*/
|
|
37
34
|
export const P2WPKH_BEACON_TX_VSIZE = 155;
|
|
38
35
|
|
|
39
36
|
/**
|
|
40
|
-
* Conservative vsize estimate for a 1-input P2PKH
|
|
37
|
+
* Conservative vsize estimate for a 1-input P2PKH to 1 P2PKH change + 1 OP_RETURN(32) tx.
|
|
41
38
|
* Legacy (non-segwit): scriptSig carries the full sig+pubkey (~108 bytes), no witness
|
|
42
39
|
* discount. Stripped ≈ 4 nVer + 1 vin-count + (32+4+1+108+4) input + 1 vout-count +
|
|
43
40
|
* 34 P2PKH-change + 43 OP_RETURN + 4 nLockTime ≈ 236 bytes. vsize = 236, rounded to 240.
|
|
@@ -51,6 +48,54 @@ export const SINGLETON_BEACON_TX_VSIZE: Readonly<Record<SingletonScriptKind, num
|
|
|
51
48
|
p2tr : P2TR_BEACON_TX_VSIZE,
|
|
52
49
|
};
|
|
53
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Serialized size (vbytes) of a single change output, by script kind:
|
|
53
|
+
* 8 (value) + 1 (scriptPubKey length) + scriptPubKey bytes. P2PKH 25, P2WPKH 22,
|
|
54
|
+
* P2TR 34. These are non-witness bytes, so each contributes its full byte count to
|
|
55
|
+
* the transaction vsize. The {@link SINGLETON_BEACON_TX_VSIZE} constants bake in a
|
|
56
|
+
* same-kind change output; {@link beaconTxVsize} uses these deltas to re-size the
|
|
57
|
+
* fee when a caller routes change to an address of a different kind (ADR 044).
|
|
58
|
+
*/
|
|
59
|
+
export const CHANGE_OUTPUT_VBYTES: Readonly<Record<SingletonScriptKind, number>> = {
|
|
60
|
+
p2pkh : 34,
|
|
61
|
+
p2wpkh : 31,
|
|
62
|
+
p2tr : 43,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Dust threshold (sats) below which a change output is not worth creating, by script
|
|
67
|
+
* kind (the standard Bitcoin Core dust relay thresholds at the default 3 sat/vB dust
|
|
68
|
+
* rate). When the change after fees falls below this, the builders omit the change
|
|
69
|
+
* output and let the remainder fall into the fee rather than emit an unspendable,
|
|
70
|
+
* relay-rejected dust output (ADR 044).
|
|
71
|
+
*/
|
|
72
|
+
export const DUST_LIMIT_SATS: Readonly<Record<SingletonScriptKind, number>> = {
|
|
73
|
+
p2pkh : 546,
|
|
74
|
+
p2wpkh : 294,
|
|
75
|
+
p2tr : 330,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* vsize (vbytes) for a beacon transaction that spends one input of `beaconKind`
|
|
80
|
+
* and returns change to an output of `changeKind`, plus the OP_RETURN(32) signal.
|
|
81
|
+
*
|
|
82
|
+
* When `changeKind === beaconKind` (the default, change to the beacon address) this
|
|
83
|
+
* returns the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant unchanged, so the
|
|
84
|
+
* default path and the constants' lock-in tests are byte-identical. A differing
|
|
85
|
+
* `changeKind` swaps the assumed same-kind change output for the actual one, keeping
|
|
86
|
+
* the result a valid upper bound. The aggregation key-path spend is the
|
|
87
|
+
* `beaconKind: 'p2tr'` case (its input is always the cohort's P2TR key path; only the
|
|
88
|
+
* change output varies), the analytical sizing ADR 045 calls for, computed without a
|
|
89
|
+
* secret.
|
|
90
|
+
*/
|
|
91
|
+
export function beaconTxVsize(
|
|
92
|
+
beaconKind: SingletonScriptKind,
|
|
93
|
+
changeKind: SingletonScriptKind,
|
|
94
|
+
): number {
|
|
95
|
+
const base = SINGLETON_BEACON_TX_VSIZE[beaconKind] - CHANGE_OUTPUT_VBYTES[beaconKind];
|
|
96
|
+
return base + CHANGE_OUTPUT_VBYTES[changeKind];
|
|
97
|
+
}
|
|
98
|
+
|
|
54
99
|
/**
|
|
55
100
|
* Detect the singleton script kind of a Bitcoin address (P2PKH / P2WPKH / P2TR).
|
|
56
101
|
* The deterministic-DID document emits all three kinds; the broadcast path needs
|
|
@@ -88,12 +133,56 @@ export function deriveSingletonAddress(
|
|
|
88
133
|
return p2tr(pubkey.slice(1, 33), undefined, network).address!;
|
|
89
134
|
}
|
|
90
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Resolve the change-output recipient for a beacon transaction. Returns the beacon
|
|
138
|
+
* address when no change address is supplied (preserving the prior behavior of
|
|
139
|
+
* returning change to the spent address), otherwise validates the caller-supplied
|
|
140
|
+
* address against the network and returns it. Validating here fails fast rather than
|
|
141
|
+
* burning a real UTXO on a transaction that breaks at broadcast (ADR 044).
|
|
142
|
+
*/
|
|
143
|
+
export function resolveChangeAddress(
|
|
144
|
+
beaconAddress: string,
|
|
145
|
+
network: BTCNetwork,
|
|
146
|
+
changeAddress?: string,
|
|
147
|
+
): string {
|
|
148
|
+
if(!changeAddress || changeAddress === beaconAddress) return beaconAddress;
|
|
149
|
+
try {
|
|
150
|
+
Address(network).decode(changeAddress);
|
|
151
|
+
} catch {
|
|
152
|
+
throw new BeaconError(
|
|
153
|
+
`Invalid change address "${changeAddress}" for network "${network}".`,
|
|
154
|
+
'INVALID_CHANGE_ADDRESS',
|
|
155
|
+
{ changeAddress, network }
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return changeAddress;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Detect the change output's script kind for fee sizing. A change address that is not
|
|
163
|
+
* one of the three singleton kinds (for example P2SH or P2WSH) is sized as P2TR, the
|
|
164
|
+
* largest standard change output, so the estimated fee stays a valid upper bound.
|
|
165
|
+
*/
|
|
166
|
+
function changeOutputKind(changeAddress: string, network: BTCNetwork): SingletonScriptKind {
|
|
167
|
+
try {
|
|
168
|
+
return detectSingletonScriptKind(changeAddress, network);
|
|
169
|
+
} catch {
|
|
170
|
+
return 'p2tr';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
91
174
|
/**
|
|
92
175
|
* Options accepted by {@link SinglePartyBeacon.buildSignAndBroadcast} and related helpers.
|
|
93
176
|
*/
|
|
94
177
|
export interface BroadcastOptions {
|
|
95
178
|
/** Fee estimator for computing the transaction fee. Defaults to {@link DEFAULT_FEE_ESTIMATOR}. */
|
|
96
179
|
feeEstimator?: FeeEstimator;
|
|
180
|
+
/**
|
|
181
|
+
* Address to send change to. Defaults to the beacon address (reuses the spent
|
|
182
|
+
* address, the prior behavior). Supply a fresh address the controller owns to
|
|
183
|
+
* stop linking the beacon's announcements into one on-chain chain (ADR 044).
|
|
184
|
+
*/
|
|
185
|
+
changeAddress?: string;
|
|
97
186
|
}
|
|
98
187
|
|
|
99
188
|
/**
|
|
@@ -107,8 +196,10 @@ export interface BeaconTxPlan {
|
|
|
107
196
|
prevOutScripts: Uint8Array[];
|
|
108
197
|
/** Amounts (sats) of the consumed previous outputs. */
|
|
109
198
|
prevOutValues: bigint[];
|
|
110
|
-
/**
|
|
199
|
+
/** The beacon address this tx spends from. */
|
|
111
200
|
beaconAddress: string;
|
|
201
|
+
/** Address the change output was sent to (the beacon address unless a change address was supplied). */
|
|
202
|
+
changeAddress: string;
|
|
112
203
|
/** The UTXO this tx consumes. */
|
|
113
204
|
utxo: AddressUtxo;
|
|
114
205
|
/** The fee (sats) already deducted from the change output. */
|
|
@@ -169,7 +260,7 @@ async function fetchSpendableUtxo(
|
|
|
169
260
|
* signing session consumes (via {@link SigningTxData}).
|
|
170
261
|
*
|
|
171
262
|
* This is the reusable counterpart to {@link SinglePartyBeacon.buildSignAndBroadcast}'s internal
|
|
172
|
-
* construction step
|
|
263
|
+
* construction step: the aggregation path must produce an unsigned tx because the
|
|
173
264
|
* signature comes from a MuSig2 round, not a local secret key.
|
|
174
265
|
*
|
|
175
266
|
* @param opts Parameters including the cohort's aggregate internal pubkey.
|
|
@@ -188,15 +279,31 @@ export async function buildAggregationBeaconTx(opts: {
|
|
|
188
279
|
network: BTCNetwork;
|
|
189
280
|
/** Optional fee estimator (defaults to 5 sat/vB). */
|
|
190
281
|
feeEstimator?: FeeEstimator;
|
|
282
|
+
/**
|
|
283
|
+
* Address to send change to. Defaults to the beacon (cohort) address. Supply the
|
|
284
|
+
* funder's address (an operator-funded cohort's funding wallet) to stop reusing the
|
|
285
|
+
* cohort address for change (ADR 044). Change ownership is the funder's call, which
|
|
286
|
+
* the cohort-condition model leaves to the caller (ADR 039).
|
|
287
|
+
*/
|
|
288
|
+
changeAddress?: string;
|
|
191
289
|
}): Promise<BeaconTxPlan> {
|
|
192
290
|
const feeEstimator = opts.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
193
291
|
const { utxo, prevTxBytes } = await fetchSpendableUtxo(opts.beaconAddress, opts.bitcoin);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
//
|
|
199
|
-
|
|
292
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, opts.network, opts.changeAddress);
|
|
293
|
+
|
|
294
|
+
// The funded beacon output is a Taproot script-tree output: key path is the
|
|
295
|
+
// MuSig2 aggregate, script path is the k-of-n fallback + CSV recovery leaves
|
|
296
|
+
// (see cohort.ts and ADR 042). Derive the witnessUtxo scriptPubKey from the
|
|
297
|
+
// funded address itself; recomputing a key-path-only p2tr(internalPubkey) here
|
|
298
|
+
// would not match the script-tree UTXO on chain and would invalidate both the
|
|
299
|
+
// key-path sighash and the fallback script-path sighash.
|
|
300
|
+
const witnessScript = OutScript.encode(Address(opts.network).decode(opts.beaconAddress));
|
|
301
|
+
|
|
302
|
+
// The fee cannot be probe-measured (no secret key until the downstream MuSig2
|
|
303
|
+
// round), so size it analytically. The input is the cohort's P2TR key path; only
|
|
304
|
+
// the change output's kind varies, so the vsize follows the change address (ADR 045).
|
|
305
|
+
const changeKind = changeOutputKind(changeAddress, opts.network);
|
|
306
|
+
const feeSats = await feeEstimator.estimateFee(beaconTxVsize('p2tr', changeKind));
|
|
200
307
|
if(BigInt(utxo.value) <= feeSats) {
|
|
201
308
|
throw new BeaconError(
|
|
202
309
|
`UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`,
|
|
@@ -216,7 +323,12 @@ export async function buildAggregationBeaconTx(opts: {
|
|
|
216
323
|
witnessUtxo : { amount: BigInt(utxo.value), script: witnessScript },
|
|
217
324
|
tapInternalKey : opts.internalPubkey,
|
|
218
325
|
});
|
|
219
|
-
|
|
326
|
+
// Change first (omitted when it would be dust, sweeping the remainder into the
|
|
327
|
+
// fee), then the OP_RETURN signal, which the spec requires to be the last output.
|
|
328
|
+
const changeValue = BigInt(utxo.value) - feeSats;
|
|
329
|
+
if(changeValue >= BigInt(DUST_LIMIT_SATS[changeKind])) {
|
|
330
|
+
tx.addOutputAddress(changeAddress, changeValue, opts.network);
|
|
331
|
+
}
|
|
220
332
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
221
333
|
|
|
222
334
|
return {
|
|
@@ -224,6 +336,7 @@ export async function buildAggregationBeaconTx(opts: {
|
|
|
224
336
|
prevOutScripts : [witnessScript],
|
|
225
337
|
prevOutValues : [BigInt(utxo.value)],
|
|
226
338
|
beaconAddress : opts.beaconAddress,
|
|
339
|
+
changeAddress,
|
|
227
340
|
utxo,
|
|
228
341
|
feeSats,
|
|
229
342
|
scriptKind : 'p2tr',
|
|
@@ -258,7 +371,7 @@ async function signSingletonInput(
|
|
|
258
371
|
// directly. We need only the sighash bytes so an external Signer can produce
|
|
259
372
|
// the signature, so we reach through the type system here. If scure ever
|
|
260
373
|
// renames this method, the P2PKH path tests fail loudly.
|
|
261
|
-
// TODO: track https://github.com/paulmillr/scure-btc-signer/issues/142
|
|
374
|
+
// TODO: track https://github.com/paulmillr/scure-btc-signer/issues/142 -
|
|
262
375
|
// drop the cast once a public preimage (e.g. `preimageP2PKH`) lands upstream.
|
|
263
376
|
const sighashType = SigHash.ALL;
|
|
264
377
|
const sighash = (tx as unknown as {
|
|
@@ -302,7 +415,7 @@ async function signSingletonInput(
|
|
|
302
415
|
// `d' = taprootTweakPrivKey(d, merkleRoot)`; the verifier checks against the
|
|
303
416
|
// tweaked output internal key `Q = P + tG`. The tweak lives inside the Signer
|
|
304
417
|
// (it needs the secret key), so we use scheme 'bip341' rather than the raw
|
|
305
|
-
// 'bip340' scheme. No script tree on singleton beacons
|
|
418
|
+
// 'bip340' scheme. No script tree on singleton beacons, no merkleRoot.
|
|
306
419
|
const sighash = tx.preimageWitnessV1(inputIdx, [prevOutScript], SigHash.DEFAULT, [amount]);
|
|
307
420
|
const sig = signer.sign(sighash, 'bip341');
|
|
308
421
|
tx.updateInput(inputIdx, { tapKeySig: sig });
|
|
@@ -377,7 +490,7 @@ export abstract class SinglePartyBeacon {
|
|
|
377
490
|
*
|
|
378
491
|
* Composed from the three extracted phases ({@link buildSinglePartyTx},
|
|
379
492
|
* {@link signSinglePartyTx}, {@link broadcastRawTx}) so each piece can be exercised
|
|
380
|
-
* in isolation. Aggregation beacons use {@link buildAggregationBeaconTx} instead
|
|
493
|
+
* in isolation. Aggregation beacons use {@link buildAggregationBeaconTx} instead:
|
|
381
494
|
* the multi-party path can't share the signing phase, but the tx-construction
|
|
382
495
|
* plumbing (UTXO fetch + OP_RETURN output + change output) is shared.
|
|
383
496
|
*
|
|
@@ -399,6 +512,7 @@ export abstract class SinglePartyBeacon {
|
|
|
399
512
|
const { utxo, prevTxBytes } = await fetchSpendableUtxo(beaconAddress, bitcoin);
|
|
400
513
|
const plan = await this.buildSinglePartyTx({
|
|
401
514
|
signalBytes, beaconAddress, utxo, prevTxBytes, signer, bitcoin, feeEstimator,
|
|
515
|
+
changeAddress : options?.changeAddress,
|
|
402
516
|
});
|
|
403
517
|
const signedHex = await this.signSinglePartyTx(plan, signer);
|
|
404
518
|
return this.broadcastRawTx(bitcoin, signedHex);
|
|
@@ -409,10 +523,11 @@ export abstract class SinglePartyBeacon {
|
|
|
409
523
|
*
|
|
410
524
|
* Detects the beacon address script kind (P2PKH / P2WPKH / P2TR) and configures
|
|
411
525
|
* the input accordingly. Validates that the signer's pubkey produces the beacon
|
|
412
|
-
* address under that script kind
|
|
526
|
+
* address under that script kind: without this check, a misconfigured caller
|
|
413
527
|
* would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
|
|
414
|
-
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant
|
|
415
|
-
* round-trip
|
|
528
|
+
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant (via {@link beaconTxVsize}),
|
|
529
|
+
* avoiding any probe-sign round-trip; a change address of a different kind re-sizes
|
|
530
|
+
* the fee by the change output's size delta so it stays a valid upper bound.
|
|
416
531
|
*/
|
|
417
532
|
protected async buildSinglePartyTx(opts: {
|
|
418
533
|
signalBytes: Uint8Array;
|
|
@@ -422,10 +537,12 @@ export abstract class SinglePartyBeacon {
|
|
|
422
537
|
signer: Signer;
|
|
423
538
|
bitcoin: BitcoinConnection;
|
|
424
539
|
feeEstimator: FeeEstimator;
|
|
540
|
+
changeAddress?: string;
|
|
425
541
|
}): Promise<BeaconTxPlan> {
|
|
426
542
|
const network = opts.bitcoin.data;
|
|
427
543
|
const pubkey = opts.signer.publicKey;
|
|
428
544
|
const kind = detectSingletonScriptKind(opts.beaconAddress, network);
|
|
545
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, network, opts.changeAddress);
|
|
429
546
|
|
|
430
547
|
const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
|
|
431
548
|
if(derivedAddress !== opts.beaconAddress) {
|
|
@@ -436,7 +553,8 @@ export abstract class SinglePartyBeacon {
|
|
|
436
553
|
);
|
|
437
554
|
}
|
|
438
555
|
|
|
439
|
-
const
|
|
556
|
+
const changeKind = changeOutputKind(changeAddress, network);
|
|
557
|
+
const feeSats = await opts.feeEstimator.estimateFee(beaconTxVsize(kind, changeKind));
|
|
440
558
|
const amount = BigInt(opts.utxo.value);
|
|
441
559
|
if(amount <= feeSats) {
|
|
442
560
|
throw new BeaconError(
|
|
@@ -482,7 +600,12 @@ export abstract class SinglePartyBeacon {
|
|
|
482
600
|
});
|
|
483
601
|
}
|
|
484
602
|
|
|
485
|
-
|
|
603
|
+
// Change first (omitted when it would be dust, sweeping the remainder into the
|
|
604
|
+
// fee), then the OP_RETURN signal, which the spec requires to be the last output.
|
|
605
|
+
const changeValue = amount - feeSats;
|
|
606
|
+
if(changeValue >= BigInt(DUST_LIMIT_SATS[changeKind])) {
|
|
607
|
+
tx.addOutputAddress(changeAddress, changeValue, network);
|
|
608
|
+
}
|
|
486
609
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
487
610
|
|
|
488
611
|
return {
|
|
@@ -490,6 +613,7 @@ export abstract class SinglePartyBeacon {
|
|
|
490
613
|
prevOutScripts : [prevOutScript],
|
|
491
614
|
prevOutValues : [amount],
|
|
492
615
|
beaconAddress : opts.beaconAddress,
|
|
616
|
+
changeAddress,
|
|
493
617
|
utxo : opts.utxo,
|
|
494
618
|
feeSats,
|
|
495
619
|
scriptKind : kind,
|
|
@@ -9,7 +9,7 @@ import { SinglePartyBeacon } from './beacon.js';
|
|
|
9
9
|
import type { BeaconService, BeaconSignal, BlockMetadata, CasPublishFn } from './interfaces.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* CAS-specific broadcast options
|
|
12
|
+
* CAS-specific broadcast options: extends {@link BroadcastOptions} with an optional
|
|
13
13
|
* `casPublish` callback used to publish the CAS Announcement off-chain after the
|
|
14
14
|
* OP_RETURN signal is broadcast.
|
|
15
15
|
*/
|
|
@@ -21,7 +21,7 @@ export interface CASBroadcastOptions extends BroadcastOptions {
|
|
|
21
21
|
* Implements {@link https://dcdpr.github.io/did-btcr2/terminology.html#cas-beacon | CAS Beacon}.
|
|
22
22
|
*
|
|
23
23
|
* A CAS (Content-Addressed Store) Beacon aggregates updates for multiple DIDs
|
|
24
|
-
* into a single CAS Announcement
|
|
24
|
+
* into a single CAS Announcement: a mapping of DIDs to their update hashes.
|
|
25
25
|
* The hash of the CAS Announcement is broadcast on-chain via OP_RETURN.
|
|
26
26
|
* During resolution, the CAS Announcement is retrieved from the sidecar (or CAS)
|
|
27
27
|
* and used to look up the individual signed update for the DID being resolved.
|
|
@@ -63,14 +63,14 @@ export class CASBeacon extends SinglePartyBeacon {
|
|
|
63
63
|
const did = this.service.id.split('#')[0];
|
|
64
64
|
|
|
65
65
|
for(const signal of signals) {
|
|
66
|
-
// Signal bytes are hex
|
|
66
|
+
// Signal bytes are hex, matches hex-keyed sidecar maps directly
|
|
67
67
|
const announcementHash = signal.signalBytes;
|
|
68
68
|
|
|
69
69
|
// Look up the CAS Announcement in sidecar casMap
|
|
70
70
|
const casAnnouncement = sidecar.casMap.get(announcementHash);
|
|
71
71
|
|
|
72
72
|
if(!casAnnouncement) {
|
|
73
|
-
// CAS Announcement not available
|
|
73
|
+
// CAS Announcement not available, emit a need
|
|
74
74
|
needs.push({
|
|
75
75
|
kind : 'NeedCASAnnouncement',
|
|
76
76
|
announcementHash,
|
|
@@ -80,10 +80,10 @@ export class CASBeacon extends SinglePartyBeacon {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Look up this DID's update hash in the CAS Announcement
|
|
83
|
-
// Announcement values are base64urlnopad per spec
|
|
83
|
+
// Announcement values are base64urlnopad per spec, convert to hex for map lookup
|
|
84
84
|
const updateHashEncoded = casAnnouncement[did];
|
|
85
85
|
|
|
86
|
-
// If no entry for this DID, this announcement doesn't contain an update for us
|
|
86
|
+
// If no entry for this DID, this announcement doesn't contain an update for us, skip
|
|
87
87
|
if(!updateHashEncoded) {
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
@@ -94,7 +94,7 @@ export class CASBeacon extends SinglePartyBeacon {
|
|
|
94
94
|
const signedUpdate = sidecar.updateMap.get(updateHash);
|
|
95
95
|
|
|
96
96
|
if(!signedUpdate) {
|
|
97
|
-
// Signed update not available
|
|
97
|
+
// Signed update not available, emit a need
|
|
98
98
|
needs.push({
|
|
99
99
|
kind : 'NeedSignedUpdate',
|
|
100
100
|
updateHash,
|
|
@@ -50,3 +50,12 @@ export class StaticFeeEstimator implements FeeEstimator {
|
|
|
50
50
|
return BigInt(Math.ceil(vsize * this.satsPerVbyte));
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Default fee estimator used when a caller supplies none: a static 5 sat/vB rate.
|
|
56
|
+
* Suitable for tests and regtest. Production callers should inject a dynamic
|
|
57
|
+
* estimator (a mempool API, or Bitcoin Core `estimatesmartfee`) at the point the
|
|
58
|
+
* beacon transaction is built (single-party broadcast options, or the aggregation
|
|
59
|
+
* service runner's fee estimator).
|
|
60
|
+
*/
|
|
61
|
+
export const DEFAULT_FEE_ESTIMATOR: FeeEstimator = new StaticFeeEstimator(5);
|
|
@@ -71,6 +71,6 @@ export interface BeaconSignal {
|
|
|
71
71
|
* The method package defines this type; the api layer provides the implementation
|
|
72
72
|
* (e.g., via CasApi.publish backed by IPFS/Helia).
|
|
73
73
|
*
|
|
74
|
-
* @param announcement The CAS Announcement object (DID
|
|
74
|
+
* @param announcement The CAS Announcement object (DID to update hash mapping).
|
|
75
75
|
*/
|
|
76
76
|
export type CasPublishFn = (announcement: Record<string, string>) => Promise<void>;
|
|
@@ -38,14 +38,14 @@ export class SingletonBeacon extends SinglePartyBeacon {
|
|
|
38
38
|
const needs = new Array<DataNeed>();
|
|
39
39
|
|
|
40
40
|
for(const signal of signals) {
|
|
41
|
-
// Signal bytes are hex
|
|
41
|
+
// Signal bytes are hex, matches hex-keyed sidecar maps directly
|
|
42
42
|
const updateHash = signal.signalBytes;
|
|
43
43
|
|
|
44
44
|
// Look up the signed update in sidecar updateMap
|
|
45
45
|
const signedUpdate = sidecar.updateMap.get(updateHash);
|
|
46
46
|
|
|
47
47
|
if(!signedUpdate) {
|
|
48
|
-
// Data not available
|
|
48
|
+
// Data not available, emit a need instead of throwing
|
|
49
49
|
needs.push({
|
|
50
50
|
kind : 'NeedSignedUpdate',
|
|
51
51
|
updateHash,
|
|
@@ -60,7 +60,7 @@ export class SMTBeacon extends SinglePartyBeacon {
|
|
|
60
60
|
const smtProof = sidecar.smtMap.get(signal.signalBytes);
|
|
61
61
|
|
|
62
62
|
if(!smtProof) {
|
|
63
|
-
// SMT Proof not available
|
|
63
|
+
// SMT Proof not available, emit a need
|
|
64
64
|
needs.push({
|
|
65
65
|
kind : 'NeedSMTProof',
|
|
66
66
|
smtRootHash : signal.signalBytes,
|
|
@@ -95,7 +95,7 @@ export class SMTBeacon extends SinglePartyBeacon {
|
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
// Non-inclusion proof verified
|
|
98
|
+
// Non-inclusion proof verified: no update for this DID this epoch, skip.
|
|
99
99
|
if(!smtProof.updateId) {
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
@@ -106,7 +106,7 @@ export class SMTBeacon extends SinglePartyBeacon {
|
|
|
106
106
|
const signedUpdate = sidecar.updateMap.get(updateHashHex);
|
|
107
107
|
|
|
108
108
|
if(!signedUpdate) {
|
|
109
|
-
// Signed update not available
|
|
109
|
+
// Signed update not available, emit a need
|
|
110
110
|
needs.push({
|
|
111
111
|
kind : 'NeedSignedUpdate',
|
|
112
112
|
updateHash : updateHashHex,
|
package/src/core/resolver.ts
CHANGED
|
@@ -164,7 +164,7 @@ export class Resolver {
|
|
|
164
164
|
#resolvedResponse: DidResolutionResponse | null = null;
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
* @internal
|
|
167
|
+
* @internal Use {@link DidBtcr2.resolve} to create instances.
|
|
168
168
|
*/
|
|
169
169
|
constructor(
|
|
170
170
|
didComponents: DidComponents,
|
|
@@ -540,13 +540,13 @@ export class Resolver {
|
|
|
540
540
|
|
|
541
541
|
/**
|
|
542
542
|
* Advance the state machine. Returns either:
|
|
543
|
-
* - `{ status: 'action-required', needs }`
|
|
544
|
-
* - `{ status: 'resolved', result }`
|
|
543
|
+
* - `{ status: 'action-required', needs }` - caller must provide data via {@link provide}
|
|
544
|
+
* - `{ status: 'resolved', result }` - resolution complete
|
|
545
545
|
*
|
|
546
546
|
* Analogous to Rust's `Resolver::resolve()`.
|
|
547
547
|
*/
|
|
548
548
|
resolve(): ResolverState {
|
|
549
|
-
// Internal loop
|
|
549
|
+
// Internal loop: keeps advancing through phases until data is needed or done
|
|
550
550
|
while(true) {
|
|
551
551
|
switch(this.#phase) {
|
|
552
552
|
|
|
@@ -554,7 +554,7 @@ export class Resolver {
|
|
|
554
554
|
// Only entered for EXTERNAL (x HRP) identifiers when genesis doc was not in sidecar.
|
|
555
555
|
case ResolverPhase.GenesisDocument: {
|
|
556
556
|
if(this.#providedGenesisDocument) {
|
|
557
|
-
// Genesis doc was provided
|
|
557
|
+
// Genesis doc was provided, establish the current document
|
|
558
558
|
this.#currentDocument = Resolver.external(
|
|
559
559
|
this.#didComponents, this.#providedGenesisDocument
|
|
560
560
|
);
|
|
@@ -595,7 +595,7 @@ export class Resolver {
|
|
|
595
595
|
};
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
// No new beacon services to query
|
|
598
|
+
// No new beacon services to query, move to processing
|
|
599
599
|
this.#phase = ResolverPhase.BeaconProcess;
|
|
600
600
|
continue;
|
|
601
601
|
}
|
|
@@ -614,10 +614,10 @@ export class Resolver {
|
|
|
614
614
|
const result = beacon.processSignals(signals, this.#sidecarData);
|
|
615
615
|
|
|
616
616
|
if(result.needs.length > 0) {
|
|
617
|
-
// This service has unmet data needs
|
|
617
|
+
// This service has unmet data needs, collect them
|
|
618
618
|
allNeeds.push(...result.needs);
|
|
619
619
|
} else {
|
|
620
|
-
// All signals for this service resolved
|
|
620
|
+
// All signals for this service resolved, collect updates, mark processed
|
|
621
621
|
this.#unsortedUpdates.push(...result.updates);
|
|
622
622
|
this.#processedServices.add(service.id);
|
|
623
623
|
}
|
package/src/core/updater.ts
CHANGED
|
@@ -116,7 +116,7 @@ export interface UpdaterParams {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Sans-I/O state machine for did:btcr2 updates
|
|
119
|
+
* Sans-I/O state machine for did:btcr2 updates: the counterpart to {@link Resolver}.
|
|
120
120
|
*
|
|
121
121
|
* Created by {@link DidBtcr2.update} (the factory). The caller drives the update by
|
|
122
122
|
* repeatedly calling {@link advance} and {@link provide}:
|
|
@@ -168,7 +168,7 @@ export class Updater {
|
|
|
168
168
|
readonly #beaconService: BeaconService;
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* @internal
|
|
171
|
+
* @internal Use {@link DidBtcr2.update} to create instances.
|
|
172
172
|
*/
|
|
173
173
|
constructor(params: UpdaterParams) {
|
|
174
174
|
this.#sourceDocument = params.sourceDocument;
|
|
@@ -216,7 +216,7 @@ export class Updater {
|
|
|
216
216
|
// checks W3C conformance but not this equality, so it's enforced explicitly here.
|
|
217
217
|
if(targetDocument.id !== sourceDocument.id) {
|
|
218
218
|
throw new UpdateError(
|
|
219
|
-
`Patches must not change the DID document id (source "${sourceDocument.id}"
|
|
219
|
+
`Patches must not change the DID document id (source "${sourceDocument.id}" to target "${targetDocument.id}").`,
|
|
220
220
|
INVALID_DID_UPDATE, { sourceId: sourceDocument.id, targetId: targetDocument.id }
|
|
221
221
|
);
|
|
222
222
|
}
|
|
@@ -315,8 +315,8 @@ export class Updater {
|
|
|
315
315
|
|
|
316
316
|
/**
|
|
317
317
|
* Advance the state machine. Returns either:
|
|
318
|
-
* - `{ status: 'action-required', needs }`
|
|
319
|
-
* - `{ status: 'complete', result }`
|
|
318
|
+
* - `{ status: 'action-required', needs }` caller must provide data via {@link provide}
|
|
319
|
+
* - `{ status: 'complete', result }` update is signed and broadcast
|
|
320
320
|
*/
|
|
321
321
|
advance(): UpdaterState {
|
|
322
322
|
while(true) {
|
|
@@ -331,7 +331,7 @@ export class Updater {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
// Phase: Sign
|
|
334
|
-
// Emit NeedSigningKey
|
|
334
|
+
// Emit NeedSigningKey: the caller supplies the secret key (or a KMS signature).
|
|
335
335
|
case 'Sign': {
|
|
336
336
|
return {
|
|
337
337
|
status : 'action-required',
|
package/src/did-btcr2.ts
CHANGED
|
@@ -135,8 +135,8 @@ export class DidBtcr2 implements DidMethod {
|
|
|
135
135
|
*
|
|
136
136
|
* Factory method that validates the update parameters and returns a sans-I/O
|
|
137
137
|
* {@link Updater} state machine. The caller drives the updater through its
|
|
138
|
-
* phases (Construct
|
|
139
|
-
* `provide()`. The method package performs **zero I/O
|
|
138
|
+
* phases (Construct -> Sign -> Broadcast -> Complete) by calling `advance()` and
|
|
139
|
+
* `provide()`. The method package performs **zero I/O**: signing key retrieval
|
|
140
140
|
* (or KMS delegation) and the on-chain broadcast are the caller's responsibility.
|
|
141
141
|
*
|
|
142
142
|
* For a fully-wired version with Bitcoin broadcast and key handling, see
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,9 @@ export * from './core/aggregation/service.js';
|
|
|
3
3
|
export * from './core/aggregation/participant.js';
|
|
4
4
|
export * from './core/aggregation/signer.js';
|
|
5
5
|
export * from './core/aggregation/conditions.js';
|
|
6
|
+
export * from './core/aggregation/recovery-policy.js';
|
|
7
|
+
export * from './core/aggregation/recovery-spend.js';
|
|
8
|
+
export * from './core/aggregation/fallback-spend.js';
|
|
6
9
|
export * from './core/aggregation/cohort.js';
|
|
7
10
|
export * from './core/aggregation/signing-session.js';
|
|
8
11
|
export * from './core/aggregation/phases.js';
|