@did-btcr2/method 0.53.0 → 0.54.1
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/README.md +6 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.mjs +1 -1
- package/dist/cjs/index.js +28 -15
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/beacon/cas-beacon.js +15 -8
- package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
- package/dist/esm/core/beacon/singleton-beacon.js +3 -3
- package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
- package/dist/esm/core/beacon/smt-beacon.js +9 -3
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/updater.js +13 -5
- package/dist/esm/core/updater.js.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +31 -3
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/cas-beacon.d.ts +15 -8
- package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/interfaces.d.ts +5 -2
- package/dist/types/core/beacon/interfaces.d.ts.map +1 -1
- package/dist/types/core/beacon/singleton-beacon.d.ts +3 -3
- package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/smt-beacon.d.ts +5 -3
- package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
- package/dist/types/core/updater.d.ts +20 -5
- package/dist/types/core/updater.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/beacon/beacon.ts +32 -3
- package/src/core/beacon/cas-beacon.ts +21 -13
- package/src/core/beacon/interfaces.ts +5 -2
- package/src/core/beacon/singleton-beacon.ts +5 -5
- package/src/core/beacon/smt-beacon.ts +12 -5
- 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,
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
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<
|
|
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":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAA4B,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC1G,OAAO,EAAe,KAAK,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;
|
|
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.
|
|
3
|
+
"version": "0.54.1",
|
|
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.
|
|
72
|
+
"@did-btcr2/bitcoin": "^0.9.0",
|
|
73
73
|
"@did-btcr2/common": "^9.1.0",
|
|
74
74
|
"@did-btcr2/keypair": "^0.13.1",
|
|
75
|
-
"@did-btcr2/
|
|
76
|
-
"@did-btcr2/
|
|
75
|
+
"@did-btcr2/cryptosuite": "^9.0.0",
|
|
76
|
+
"@did-btcr2/smt": "^0.3.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<
|
|
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<
|
|
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
|
|
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,
|
|
138
|
-
* announcement via
|
|
139
|
-
*
|
|
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<
|
|
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<
|
|
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
|
-
//
|
|
169
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
* (e.g.,
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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
|
}
|
package/src/core/updater.ts
CHANGED
|
@@ -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,
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
|
|
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
|