@did-btcr2/method 0.53.0 → 0.54.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.
Files changed (33) hide show
  1. package/README.md +6 -2
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/browser.js +1 -1
  4. package/dist/browser.mjs +1 -1
  5. package/dist/cjs/index.js +28 -15
  6. package/dist/esm/core/beacon/beacon.js.map +1 -1
  7. package/dist/esm/core/beacon/cas-beacon.js +15 -8
  8. package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
  9. package/dist/esm/core/beacon/singleton-beacon.js +3 -3
  10. package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
  11. package/dist/esm/core/beacon/smt-beacon.js +9 -3
  12. package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
  13. package/dist/esm/core/updater.js +13 -5
  14. package/dist/esm/core/updater.js.map +1 -1
  15. package/dist/types/core/beacon/beacon.d.ts +31 -3
  16. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  17. package/dist/types/core/beacon/cas-beacon.d.ts +15 -8
  18. package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
  19. package/dist/types/core/beacon/interfaces.d.ts +5 -2
  20. package/dist/types/core/beacon/interfaces.d.ts.map +1 -1
  21. package/dist/types/core/beacon/singleton-beacon.d.ts +3 -3
  22. package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -1
  23. package/dist/types/core/beacon/smt-beacon.d.ts +5 -3
  24. package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
  25. package/dist/types/core/updater.d.ts +20 -5
  26. package/dist/types/core/updater.d.ts.map +1 -1
  27. package/package.json +4 -4
  28. package/src/core/beacon/beacon.ts +32 -3
  29. package/src/core/beacon/cas-beacon.ts +21 -13
  30. package/src/core/beacon/interfaces.ts +5 -2
  31. package/src/core/beacon/singleton-beacon.ts +5 -5
  32. package/src/core/beacon/smt-beacon.ts +12 -5
  33. package/src/core/updater.ts +23 -7
@@ -3,6 +3,8 @@ import type { PatchOperation } from '@did-btcr2/common';
3
3
  import type { Signer } from '@did-btcr2/keypair';
4
4
  import type { SignedBTCR2Update, UnsignedBTCR2Update } from './btcr2-update.js';
5
5
  import { type Btcr2DidDocument, type DidVerificationMethod } from '../utils/did-document.js';
6
+ import type { BroadcastResult } from './beacon/beacon.js';
7
+ import type { CASBroadcastOptions } from './beacon/cas-beacon.js';
6
8
  import type { BeaconService } from './beacon/interfaces.js';
7
9
  /**
8
10
  * The updater needs the caller to supply a {@link Signer} for the given
@@ -47,10 +49,15 @@ export interface FundingProof {
47
49
  * The updater needs the caller to broadcast the signed update via the beacon.
48
50
  *
49
51
  * The caller decides how: for single-party beacons, call
50
- * `Updater.announce(beaconService, signedUpdate, secretKey, bitcoin)` or
52
+ * `Updater.announce(beaconService, signedUpdate, signer, bitcoin, options?)` or
51
53
  * `BeaconFactory.establish(beaconService).broadcastSignal(...)`. For multi-party
52
54
  * aggregate beacons, hand off to the aggregation protocol.
53
55
  *
56
+ * Both single-party paths return a `BroadcastResult`; capture it. Beyond the txid,
57
+ * it carries the per-beacon-type sidecar artifacts: the CAS Announcement (CAS
58
+ * beacons) and the SMT inclusion proof (SMT beacons), whose embedded nonce exists
59
+ * nowhere else, so a discarded proof makes the signal permanently unresolvable.
60
+ *
54
61
  * After the broadcast succeeds, the caller calls `updater.provide(need)` (with no
55
62
  * data) to transition the updater to Complete.
56
63
  */
@@ -113,10 +120,14 @@ export interface UpdaterParams {
113
120
  * // Check UTXOs at need.beaconAddress, fund if needed
114
121
  * updater.provide(need);
115
122
  * break;
116
- * case 'NeedBroadcast':
117
- * await Updater.announce(need.beaconService, need.signedUpdate, signer, bitcoin);
123
+ * case 'NeedBroadcast': {
124
+ * // Capture the BroadcastResult: broadcast.txid, plus broadcast.announcement
125
+ * // (CAS beacons) and broadcast.proof (SMT beacons; must be retained, the
126
+ * // proof's nonce exists nowhere else).
127
+ * const broadcast = await Updater.announce(need.beaconService, need.signedUpdate, signer, bitcoin);
118
128
  * updater.provide(need);
119
129
  * break;
130
+ * }
120
131
  * }
121
132
  * }
122
133
  * state = updater.advance();
@@ -170,9 +181,13 @@ export declare class Updater {
170
181
  * @param {SignedBTCR2Update} update The signed update to announce.
171
182
  * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
172
183
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
173
- * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
184
+ * @param {CASBroadcastOptions} [options] Optional broadcast configuration (fee estimator,
185
+ * change address, and, for CAS beacons, a `casPublish` callback invoked before the
186
+ * transaction broadcast; other beacon types ignore `casPublish`).
187
+ * @returns {Promise<BroadcastResult>} The broadcast artifacts: the signed update, the signal
188
+ * txid, and any per-beacon-type sidecar data (CAS announcement / SMT proof).
174
189
  */
175
- static announce(beaconService: BeaconService, update: SignedBTCR2Update, signer: Signer, bitcoin: BitcoinConnection): Promise<SignedBTCR2Update>;
190
+ static announce(beaconService: BeaconService, update: SignedBTCR2Update, signer: Signer, bitcoin: BitcoinConnection, options?: CASBroadcastOptions): Promise<BroadcastResult>;
176
191
  /**
177
192
  * Advance the state machine. Returns either:
178
193
  * - `{ status: 'action-required', needs }` caller must provide data via {@link provide}
@@ -1 +1 @@
1
- {"version":3,"file":"updater.d.ts","sourceRoot":"","sources":["../../../src/core/updater.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"updater.d.ts","sourceRoot":"","sources":["../../../src/core/updater.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@did-btcr2/method",
3
- "version": "0.53.0",
3
+ "version": "0.54.0",
4
4
  "type": "module",
5
5
  "description": "Reference implementation for the did:btcr2 DID method written in TypeScript and JavaScript. did:btcr2 is a censorship resistant DID Method using the Bitcoin blockchain as a Verifiable Data Registry to announce changes to the DID document. This is the core method implementation for the did-btcr2-js monorepo.",
6
6
  "main": "./dist/cjs/index.js",
@@ -69,11 +69,11 @@
69
69
  "@scure/base": "^1.2.6",
70
70
  "@scure/btc-signer": "^1.8.1",
71
71
  "@web5/dids": "^1.2.0",
72
- "@did-btcr2/bitcoin": "^0.8.0",
73
72
  "@did-btcr2/common": "^9.1.0",
74
- "@did-btcr2/keypair": "^0.13.1",
75
73
  "@did-btcr2/smt": "^0.3.0",
76
- "@did-btcr2/cryptosuite": "^9.0.0"
74
+ "@did-btcr2/cryptosuite": "^9.0.0",
75
+ "@did-btcr2/keypair": "^0.13.1",
76
+ "@did-btcr2/bitcoin": "^0.8.0"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@eslint/js": "^9.39.4",
@@ -4,8 +4,9 @@ import type { SignedBTCR2Update } from '../btcr2-update.js';
4
4
  import type { Signer } from '@did-btcr2/keypair';
5
5
  import { concatBytes, hexToBytes } from '@noble/hashes/utils.js';
6
6
  import { Address, OutScript, p2pkh, p2tr, p2wpkh, Script, SigHash, Transaction } from '@scure/btc-signer';
7
+ import type { SMTProof } from '../interfaces.js';
7
8
  import type { BeaconProcessResult } from '../resolver.js';
8
- import type { SidecarData } from '../types.js';
9
+ import type { CASAnnouncement, SidecarData } from '../types.js';
9
10
  import { BeaconError } from './error.js';
10
11
  import { DEFAULT_FEE_ESTIMATOR } from './fee-estimator.js';
11
12
  import type { FeeEstimator } from './fee-estimator.js';
@@ -185,6 +186,33 @@ export interface BroadcastOptions {
185
186
  changeAddress?: string;
186
187
  }
187
188
 
189
+ /**
190
+ * Result of a single-party beacon broadcast. Beyond the signed update itself,
191
+ * it carries every artifact the broadcast produced that a resolver will later
192
+ * need: without them, some signals are unresolvable (the SMT proof) or the
193
+ * controller cannot distribute the sidecar data the spec requires them to
194
+ * retain (the CAS announcement).
195
+ */
196
+ export interface BroadcastResult {
197
+ /** The signed update that was broadcast. */
198
+ signedUpdate: SignedBTCR2Update;
199
+ /** Transaction id of the on-chain beacon signal. */
200
+ txid: string;
201
+ /**
202
+ * The CAS Announcement whose hash rode in the OP_RETURN output. CAS beacons
203
+ * only. Capture it for sidecar distribution (or publish it to a CAS): a
204
+ * resolver cannot process the signal without it.
205
+ */
206
+ announcement?: CASAnnouncement;
207
+ /**
208
+ * SMT inclusion proof for the broadcast update, including the nonce the leaf
209
+ * was blinded with. SMT beacons only. Capture it for sidecar distribution:
210
+ * the nonce is generated at broadcast time and appears nowhere else, so a
211
+ * signal whose proof is dropped is permanently unresolvable.
212
+ */
213
+ proof?: SMTProof;
214
+ }
215
+
188
216
  /**
189
217
  * Unsigned beacon transaction + the prev-output metadata needed for downstream
190
218
  * signing (single-party ECDSA or multi-party MuSig2 Taproot).
@@ -544,14 +572,15 @@ export abstract class SinglePartyBeacon {
544
572
  * ECDSA for P2PKH / P2WPKH singletons, Schnorr (BIP-340) for P2TR key-path.
545
573
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
546
574
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
547
- * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
575
+ * @returns {Promise<BroadcastResult>} The broadcast artifacts: the signed update, the
576
+ * signal txid, and the per-beacon-type sidecar data (CAS announcement / SMT proof).
548
577
  */
549
578
  abstract broadcastSignal(
550
579
  signedUpdate: SignedBTCR2Update,
551
580
  signer: Signer,
552
581
  bitcoin: BitcoinConnection,
553
582
  options?: BroadcastOptions
554
- ): Promise<SignedBTCR2Update>;
583
+ ): Promise<BroadcastResult>;
555
584
 
556
585
  /**
557
586
  * Build + sign + broadcast a singleton beacon signal transaction. The beacon
@@ -4,14 +4,15 @@ import type { SignedBTCR2Update } from '../btcr2-update.js';
4
4
  import type { Signer } from '@did-btcr2/keypair';
5
5
  import type { BeaconProcessResult, DataNeed } from '../resolver.js';
6
6
  import type { SidecarData } from '../types.js';
7
- import type { BroadcastOptions } from './beacon.js';
7
+ import type { BroadcastOptions, BroadcastResult } from './beacon.js';
8
8
  import { SinglePartyBeacon } from './beacon.js';
9
9
  import type { BeaconService, BeaconSignal, BlockMetadata, CasPublishFn } from './interfaces.js';
10
10
 
11
11
  /**
12
12
  * CAS-specific broadcast options: extends {@link BroadcastOptions} with an optional
13
- * `casPublish` callback used to publish the CAS Announcement off-chain after the
14
- * OP_RETURN signal is broadcast.
13
+ * `casPublish` callback used to publish the CAS Announcement off-chain before the
14
+ * OP_RETURN signal transaction is broadcast. A publish failure aborts the broadcast
15
+ * while the beacon UTXO is still unspent.
15
16
  */
16
17
  export interface CASBroadcastOptions extends BroadcastOptions {
17
18
  casPublish?: CasPublishFn;
@@ -134,17 +135,23 @@ export class CASBeacon extends SinglePartyBeacon {
134
135
  /**
135
136
  * Broadcasts a CAS Beacon signal to the Bitcoin network.
136
137
  *
137
- * Creates a CAS Announcement mapping the DID to the update hash, broadcasts the hash of the
138
- * announcement via OP_RETURN, and optionally publishes the announcement off-chain via the
139
- * supplied `casPublish` callback. UTXO selection, PSBT construction, fee estimation, signing,
138
+ * Creates a CAS Announcement mapping the DID to the update hash, optionally publishes the
139
+ * announcement off-chain via the supplied `casPublish` callback, then broadcasts the hash of
140
+ * the announcement via OP_RETURN. UTXO selection, PSBT construction, fee estimation, signing,
140
141
  * and broadcast are delegated to {@link SinglePartyBeacon.buildSignAndBroadcast}.
141
142
  *
143
+ * The CAS publish happens **before** the transaction broadcast: a publish failure aborts the
144
+ * operation while the beacon UTXO is still unspent, so no on-chain signal ever points at an
145
+ * announcement that failed to publish. The announcement is content-addressed, so a retry
146
+ * after a failed broadcast re-publishes the same bytes to the same address (idempotent).
147
+ *
142
148
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
143
149
  * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
144
150
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
145
151
  * @param {CASBroadcastOptions} [options] Optional broadcast configuration, including a
146
152
  * `casPublish` callback to publish the announcement off-chain and a `feeEstimator`.
147
- * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
153
+ * @returns {Promise<BroadcastResult>} The signed update, the signal txid, and the CAS
154
+ * Announcement (capture it for sidecar distribution when no `casPublish` is supplied).
148
155
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
149
156
  */
150
157
  async broadcastSignal(
@@ -152,7 +159,7 @@ export class CASBeacon extends SinglePartyBeacon {
152
159
  signer: Signer,
153
160
  bitcoin: BitcoinConnection,
154
161
  options?: CASBroadcastOptions
155
- ): Promise<SignedBTCR2Update> {
162
+ ): Promise<BroadcastResult> {
156
163
  // Extract the DID from the beacon service id (strip the #fragment)
157
164
  const did = this.service.id.split('#')[0];
158
165
 
@@ -165,14 +172,15 @@ export class CASBeacon extends SinglePartyBeacon {
165
172
  // Canonicalize and hash the CAS Announcement for the OP_RETURN output
166
173
  const announcementHash = hash(canonicalize(casAnnouncement));
167
174
 
168
- // Delegate UTXO selection, PSBT construction, fee estimation, signing, and broadcast
169
- await this.buildSignAndBroadcast(announcementHash, signer, bitcoin, options);
170
-
171
- // Publish CAS Announcement to content-addressed store if callback provided
175
+ // Publish the announcement to the content-addressed store before spending the
176
+ // beacon UTXO, so a publish failure aborts pre-spend.
172
177
  if(options?.casPublish) {
173
178
  await options.casPublish(casAnnouncement);
174
179
  }
175
180
 
176
- return signedUpdate;
181
+ // Delegate UTXO selection, PSBT construction, fee estimation, signing, and broadcast
182
+ const txid = await this.buildSignAndBroadcast(announcementHash, signer, bitcoin, options);
183
+
184
+ return { signedUpdate, txid, announcement: casAnnouncement };
177
185
  }
178
186
  }
@@ -68,8 +68,11 @@ export interface BeaconSignal {
68
68
 
69
69
  /**
70
70
  * Callback for publishing a CAS Announcement to a content-addressed store.
71
- * The method package defines this type; the api layer provides the implementation
72
- * (e.g., via CasApi.publish backed by IPFS/Helia).
71
+ * The method package defines this type; the caller provides the implementation
72
+ * (e.g., the api layer's CasApi.publish backed by any CAS executor).
73
+ *
74
+ * Invoked before the beacon signal transaction is broadcast, so a thrown error
75
+ * aborts the operation while the beacon UTXO is still unspent.
73
76
  *
74
77
  * @param announcement The CAS Announcement object (DID to update hash mapping).
75
78
  */
@@ -4,7 +4,7 @@ import type { SignedBTCR2Update } from '../btcr2-update.js';
4
4
  import type { Signer } from '@did-btcr2/keypair';
5
5
  import type { BeaconProcessResult, DataNeed } from '../resolver.js';
6
6
  import type { SidecarData } from '../types.js';
7
- import type { BroadcastOptions } from './beacon.js';
7
+ import type { BroadcastOptions, BroadcastResult } from './beacon.js';
8
8
  import { SinglePartyBeacon } from './beacon.js';
9
9
  import type { BeaconService, BeaconSignal, BlockMetadata } from './interfaces.js';
10
10
 
@@ -70,7 +70,7 @@ export class SingletonBeacon extends SinglePartyBeacon {
70
70
  * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
71
71
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
72
72
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
73
- * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
73
+ * @returns {Promise<BroadcastResult>} The signed update and the signal txid.
74
74
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
75
75
  */
76
76
  async broadcastSignal(
@@ -78,9 +78,9 @@ export class SingletonBeacon extends SinglePartyBeacon {
78
78
  signer: Signer,
79
79
  bitcoin: BitcoinConnection,
80
80
  options?: BroadcastOptions
81
- ): Promise<SignedBTCR2Update> {
81
+ ): Promise<BroadcastResult> {
82
82
  const signalBytes = hash(canonicalize(signedUpdate));
83
- await this.buildSignAndBroadcast(signalBytes, signer, bitcoin, options);
84
- return signedUpdate;
83
+ const txid = await this.buildSignAndBroadcast(signalBytes, signer, bitcoin, options);
84
+ return { signedUpdate, txid };
85
85
  }
86
86
  }
@@ -6,7 +6,7 @@ import { base64UrlToHash, blockHash, BTCR2MerkleTree, didToIndex, hashToHex, ver
6
6
  import { randomBytes } from '@noble/hashes/utils';
7
7
  import type { BeaconProcessResult, DataNeed } from '../resolver.js';
8
8
  import type { SidecarData } from '../types.js';
9
- import type { BroadcastOptions } from './beacon.js';
9
+ import type { BroadcastOptions, BroadcastResult } from './beacon.js';
10
10
  import { SinglePartyBeacon } from './beacon.js';
11
11
  import { SMTBeaconError } from './error.js';
12
12
  import type { BeaconService, BeaconSignal, BlockMetadata } from './interfaces.js';
@@ -133,7 +133,9 @@ export class SMTBeacon extends SinglePartyBeacon {
133
133
  * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
134
134
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
135
135
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
136
- * @return {Promise<SignedBTCR2Update>} The signed update that was broadcast.
136
+ * @return {Promise<BroadcastResult>} The signed update, the signal txid, and the SMT
137
+ * inclusion proof (with the leaf nonce embedded). The proof MUST be captured for sidecar
138
+ * distribution: the nonce exists only here, so the on-chain signal is unresolvable without it.
137
139
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
138
140
  */
139
141
  async broadcastSignal(
@@ -141,7 +143,7 @@ export class SMTBeacon extends SinglePartyBeacon {
141
143
  signer: Signer,
142
144
  bitcoin: BitcoinConnection,
143
145
  options?: BroadcastOptions
144
- ): Promise<SignedBTCR2Update> {
146
+ ): Promise<BroadcastResult> {
145
147
  // Extract the DID from the beacon service id (strip the #fragment)
146
148
  const did = this.service.id.split('#')[0];
147
149
 
@@ -152,9 +154,14 @@ export class SMTBeacon extends SinglePartyBeacon {
152
154
  tree.addEntries([{ did, nonce, signedUpdate: canonicalBytes }]);
153
155
  tree.finalize();
154
156
 
157
+ // Serialize the inclusion proof (carrying the nonce and updateId) before
158
+ // broadcasting: it is the only artifact that can link the on-chain root back
159
+ // to the update, and the nonce it embeds is irrecoverable once dropped.
160
+ const proof = tree.proof(did);
161
+
155
162
  // Root hash is the signal bytes for the OP_RETURN output
156
- await this.buildSignAndBroadcast(tree.rootHash, signer, bitcoin, options);
163
+ const txid = await this.buildSignAndBroadcast(tree.rootHash, signer, bitcoin, options);
157
164
 
158
- return signedUpdate;
165
+ return { signedUpdate, txid, proof };
159
166
  }
160
167
  }
@@ -5,6 +5,8 @@ import { SchnorrMultikey } from '@did-btcr2/cryptosuite';
5
5
  import type { Signer } from '@did-btcr2/keypair';
6
6
  import type { Btcr2DataIntegrityConfig, SignedBTCR2Update, UnsignedBTCR2Update } from './btcr2-update.js';
7
7
  import { DidDocument, type Btcr2DidDocument, type DidVerificationMethod } from '../utils/did-document.js';
8
+ import type { BroadcastResult } from './beacon/beacon.js';
9
+ import type { CASBroadcastOptions } from './beacon/cas-beacon.js';
8
10
  import { BeaconFactory } from './beacon/factory.js';
9
11
  import type { BeaconService } from './beacon/interfaces.js';
10
12
 
@@ -56,10 +58,15 @@ export interface FundingProof {
56
58
  * The updater needs the caller to broadcast the signed update via the beacon.
57
59
  *
58
60
  * The caller decides how: for single-party beacons, call
59
- * `Updater.announce(beaconService, signedUpdate, secretKey, bitcoin)` or
61
+ * `Updater.announce(beaconService, signedUpdate, signer, bitcoin, options?)` or
60
62
  * `BeaconFactory.establish(beaconService).broadcastSignal(...)`. For multi-party
61
63
  * aggregate beacons, hand off to the aggregation protocol.
62
64
  *
65
+ * Both single-party paths return a `BroadcastResult`; capture it. Beyond the txid,
66
+ * it carries the per-beacon-type sidecar artifacts: the CAS Announcement (CAS
67
+ * beacons) and the SMT inclusion proof (SMT beacons), whose embedded nonce exists
68
+ * nowhere else, so a discarded proof makes the signal permanently unresolvable.
69
+ *
63
70
  * After the broadcast succeeds, the caller calls `updater.provide(need)` (with no
64
71
  * data) to transition the updater to Complete.
65
72
  */
@@ -137,10 +144,14 @@ export interface UpdaterParams {
137
144
  * // Check UTXOs at need.beaconAddress, fund if needed
138
145
  * updater.provide(need);
139
146
  * break;
140
- * case 'NeedBroadcast':
141
- * await Updater.announce(need.beaconService, need.signedUpdate, signer, bitcoin);
147
+ * case 'NeedBroadcast': {
148
+ * // Capture the BroadcastResult: broadcast.txid, plus broadcast.announcement
149
+ * // (CAS beacons) and broadcast.proof (SMT beacons; must be retained, the
150
+ * // proof's nonce exists nowhere else).
151
+ * const broadcast = await Updater.announce(need.beaconService, need.signedUpdate, signer, bitcoin);
142
152
  * updater.provide(need);
143
153
  * break;
154
+ * }
144
155
  * }
145
156
  * }
146
157
  * state = updater.advance();
@@ -315,16 +326,21 @@ export class Updater {
315
326
  * @param {SignedBTCR2Update} update The signed update to announce.
316
327
  * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
317
328
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
318
- * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
329
+ * @param {CASBroadcastOptions} [options] Optional broadcast configuration (fee estimator,
330
+ * change address, and, for CAS beacons, a `casPublish` callback invoked before the
331
+ * transaction broadcast; other beacon types ignore `casPublish`).
332
+ * @returns {Promise<BroadcastResult>} The broadcast artifacts: the signed update, the signal
333
+ * txid, and any per-beacon-type sidecar data (CAS announcement / SMT proof).
319
334
  */
320
335
  static async announce(
321
336
  beaconService: BeaconService,
322
337
  update: SignedBTCR2Update,
323
338
  signer: Signer,
324
- bitcoin: BitcoinConnection
325
- ): Promise<SignedBTCR2Update> {
339
+ bitcoin: BitcoinConnection,
340
+ options?: CASBroadcastOptions
341
+ ): Promise<BroadcastResult> {
326
342
  const beacon = BeaconFactory.establish(beaconService);
327
- return beacon.broadcastSignal(update, signer, bitcoin);
343
+ return beacon.broadcastSignal(update, signer, bitcoin, options);
328
344
  }
329
345
 
330
346
  // Private instance wrappers