@did-btcr2/method 0.29.0 → 0.33.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/README.md +36 -16
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +6763 -6170
- package/dist/browser.mjs +6763 -6170
- package/dist/cjs/index.js +1860 -467
- package/dist/esm/core/aggregation/beacon-strategy.js +5 -4
- package/dist/esm/core/aggregation/beacon-strategy.js.map +1 -1
- package/dist/esm/core/aggregation/transport/factory.js +15 -6
- package/dist/esm/core/aggregation/transport/factory.js.map +1 -1
- package/dist/esm/core/aggregation/transport/http/client.js +350 -0
- package/dist/esm/core/aggregation/transport/http/client.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/envelope.js +126 -0
- package/dist/esm/core/aggregation/transport/http/envelope.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/errors.js +11 -0
- package/dist/esm/core/aggregation/transport/http/errors.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +45 -0
- package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/index.js +12 -0
- package/dist/esm/core/aggregation/transport/http/index.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/nonce-cache.js +38 -0
- package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/protocol.js +28 -0
- package/dist/esm/core/aggregation/transport/http/protocol.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/rate-limiter.js +45 -0
- package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/request-auth.js +100 -0
- package/dist/esm/core/aggregation/transport/http/request-auth.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/server.js +481 -0
- package/dist/esm/core/aggregation/transport/http/server.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/sse-stream.js +110 -0
- package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +1 -0
- package/dist/esm/core/aggregation/transport/http/sse-writer.js +25 -0
- package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +1 -0
- package/dist/esm/core/aggregation/transport/index.js +1 -0
- package/dist/esm/core/aggregation/transport/index.js.map +1 -1
- package/dist/esm/core/beacon/beacon.js +197 -51
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/beacon/cas-beacon.js +3 -3
- 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 +22 -14
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/resolver.js +7 -4
- package/dist/esm/core/resolver.js.map +1 -1
- package/dist/esm/core/updater.js +63 -55
- package/dist/esm/core/updater.js.map +1 -1
- package/dist/types/core/aggregation/beacon-strategy.d.ts.map +1 -1
- package/dist/types/core/aggregation/transport/factory.d.ts +22 -7
- package/dist/types/core/aggregation/transport/factory.d.ts.map +1 -1
- package/dist/types/core/aggregation/transport/http/client.d.ts +48 -0
- package/dist/types/core/aggregation/transport/http/client.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/envelope.d.ts +64 -0
- package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/errors.d.ts +9 -0
- package/dist/types/core/aggregation/transport/http/errors.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +32 -0
- package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/index.d.ts +12 -0
- package/dist/types/core/aggregation/transport/http/index.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +26 -0
- package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/protocol.d.ts +53 -0
- package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +41 -0
- package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/request-auth.d.ts +50 -0
- package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/server.d.ts +110 -0
- package/dist/types/core/aggregation/transport/http/server.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +34 -0
- package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +12 -0
- package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +1 -0
- package/dist/types/core/aggregation/transport/index.d.ts +1 -0
- package/dist/types/core/aggregation/transport/index.d.ts.map +1 -1
- package/dist/types/core/aggregation/transport/transport.d.ts +1 -1
- package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +72 -12
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/cas-beacon.d.ts +3 -3
- package/dist/types/core/beacon/cas-beacon.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 +3 -3
- package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
- package/dist/types/core/interfaces.d.ts +14 -11
- package/dist/types/core/interfaces.d.ts.map +1 -1
- package/dist/types/core/resolver.d.ts.map +1 -1
- package/dist/types/core/updater.d.ts +27 -12
- package/dist/types/core/updater.d.ts.map +1 -1
- package/package.json +20 -8
- package/src/core/aggregation/beacon-strategy.ts +5 -4
- package/src/core/aggregation/transport/factory.ts +48 -12
- package/src/core/aggregation/transport/http/client.ts +409 -0
- package/src/core/aggregation/transport/http/envelope.ts +204 -0
- package/src/core/aggregation/transport/http/errors.ts +11 -0
- package/src/core/aggregation/transport/http/inbox-buffer.ts +53 -0
- package/src/core/aggregation/transport/http/index.ts +11 -0
- package/src/core/aggregation/transport/http/nonce-cache.ts +43 -0
- package/src/core/aggregation/transport/http/protocol.ts +57 -0
- package/src/core/aggregation/transport/http/rate-limiter.ts +75 -0
- package/src/core/aggregation/transport/http/request-auth.ts +164 -0
- package/src/core/aggregation/transport/http/server.ts +615 -0
- package/src/core/aggregation/transport/http/sse-stream.ts +121 -0
- package/src/core/aggregation/transport/http/sse-writer.ts +23 -0
- package/src/core/aggregation/transport/index.ts +1 -0
- package/src/core/aggregation/transport/transport.ts +1 -1
- package/src/core/beacon/beacon.ts +255 -64
- package/src/core/beacon/cas-beacon.ts +4 -4
- package/src/core/beacon/singleton-beacon.ts +4 -4
- package/src/core/beacon/smt-beacon.ts +24 -16
- package/src/core/interfaces.ts +14 -11
- package/src/core/resolver.ts +8 -5
- package/src/core/updater.ts +113 -67
|
@@ -1,24 +1,78 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { OP, p2tr, p2wpkh, Script, Transaction } from '@scure/btc-signer';
|
|
1
|
+
import { concatBytes, hexToBytes } from '@noble/hashes/utils.js';
|
|
2
|
+
import { Address, OutScript, p2pkh, p2tr, p2wpkh, Script, SigHash, Transaction } from '@scure/btc-signer';
|
|
4
3
|
import { BeaconError } from './error.js';
|
|
5
4
|
import { StaticFeeEstimator } from './fee-estimator.js';
|
|
6
5
|
/** Default fee estimator used when none is supplied. ~5 sat/vB static rate. */
|
|
7
6
|
const DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
|
|
8
7
|
/**
|
|
9
8
|
* Conservative vsize estimate for a 1-input P2TR key-path → 1 P2TR change + 1 OP_RETURN(32) tx.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* signatures are produced externally.
|
|
9
|
+
* Stripped 137 + witness ≈ 68 (marker + flag + stack-count + sig-len + 64 BIP-340 sig).
|
|
10
|
+
* Weight = 137*4 + 68 = 616, vsize ≈ 154, rounded to 160 for headroom.
|
|
13
11
|
*/
|
|
14
|
-
const P2TR_BEACON_TX_VSIZE =
|
|
12
|
+
export const P2TR_BEACON_TX_VSIZE = 160;
|
|
13
|
+
/**
|
|
14
|
+
* Conservative vsize estimate for a 1-input P2WPKH → 1 P2WPKH change + 1 OP_RETURN(32) tx.
|
|
15
|
+
* Stripped 125 + witness ≈ 110 (worst-case DER ECDSA sig 72 + sighash byte + 33 pubkey + framing).
|
|
16
|
+
* vsize = ceil((125*4 + 110) / 4) ≈ 153, rounded to 155.
|
|
17
|
+
*/
|
|
18
|
+
export const P2WPKH_BEACON_TX_VSIZE = 155;
|
|
19
|
+
/**
|
|
20
|
+
* Conservative vsize estimate for a 1-input P2PKH → 1 P2PKH change + 1 OP_RETURN(32) tx.
|
|
21
|
+
* Legacy (non-segwit): scriptSig carries the full sig+pubkey (~108 bytes), no witness
|
|
22
|
+
* discount. Stripped ≈ 4 nVer + 1 vin-count + (32+4+1+108+4) input + 1 vout-count +
|
|
23
|
+
* 34 P2PKH-change + 43 OP_RETURN + 4 nLockTime ≈ 236 bytes. vsize = 236, rounded to 240.
|
|
24
|
+
*/
|
|
25
|
+
export const P2PKH_BEACON_TX_VSIZE = 240;
|
|
26
|
+
/** Per-kind vsize lookup for singleton beacon fee estimation. */
|
|
27
|
+
export const SINGLETON_BEACON_TX_VSIZE = {
|
|
28
|
+
p2pkh: P2PKH_BEACON_TX_VSIZE,
|
|
29
|
+
p2wpkh: P2WPKH_BEACON_TX_VSIZE,
|
|
30
|
+
p2tr: P2TR_BEACON_TX_VSIZE,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Detect the singleton script kind of a Bitcoin address (P2PKH / P2WPKH / P2TR).
|
|
34
|
+
* The deterministic-DID document emits all three kinds; the broadcast path needs
|
|
35
|
+
* to know which is in use to construct the input and dispatch the signing primitive.
|
|
36
|
+
*/
|
|
37
|
+
export function detectSingletonScriptKind(bitcoinAddress, network) {
|
|
38
|
+
const decoded = Address(network).decode(bitcoinAddress);
|
|
39
|
+
if (decoded.type === 'pkh')
|
|
40
|
+
return 'p2pkh';
|
|
41
|
+
if (decoded.type === 'wpkh')
|
|
42
|
+
return 'p2wpkh';
|
|
43
|
+
if (decoded.type === 'tr')
|
|
44
|
+
return 'p2tr';
|
|
45
|
+
throw new BeaconError(`Unsupported singleton beacon address type "${decoded.type}". `
|
|
46
|
+
+ 'Expected P2PKH, P2WPKH, or P2TR (taproot key-path).', 'UNSUPPORTED_BEACON_ADDRESS_TYPE', { address: bitcoinAddress, kind: decoded.type });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Derive the address that `pubkey` produces under the given script kind. Used to
|
|
50
|
+
* fail-fast when a caller wires a signer to a beacon address that the signer's
|
|
51
|
+
* pubkey cannot actually spend.
|
|
52
|
+
*/
|
|
53
|
+
export function deriveSingletonAddress(kind, pubkey, network) {
|
|
54
|
+
if (kind === 'p2pkh')
|
|
55
|
+
return p2pkh(pubkey, network).address;
|
|
56
|
+
if (kind === 'p2wpkh')
|
|
57
|
+
return p2wpkh(pubkey, network).address;
|
|
58
|
+
// P2TR key-path: x-only internal key (drop the SEC prefix byte).
|
|
59
|
+
return p2tr(pubkey.slice(1, 33), undefined, network).address;
|
|
60
|
+
}
|
|
15
61
|
/**
|
|
16
62
|
* Build an OP_RETURN script carrying a 32-byte beacon signal.
|
|
17
63
|
* Exported as a utility so callers building txs outside Beacon (e.g., the aggregation
|
|
18
64
|
* `onProvideTxData` callback) can produce identical output.
|
|
65
|
+
*
|
|
66
|
+
* Uses the opcode *string* `'RETURN'` rather than the numeric `OP.RETURN`
|
|
67
|
+
* constant because scure's `Script.encode` interprets a number as a byte to
|
|
68
|
+
* push, not as the opcode. The string form emits the bare opcode (0x6a)
|
|
69
|
+
* followed by an `OP_PUSHBYTES_32` push, producing the standard NULL_DATA
|
|
70
|
+
* shape Bitcoin Core's `IsStandard` accepts. The numeric form silently
|
|
71
|
+
* produces `OP_PUSHBYTES_1 0x6a OP_PUSHBYTES_32 <32 bytes>`, which is
|
|
72
|
+
* non-standard and rejected at broadcast with `RPC error -26: scriptpubkey`.
|
|
19
73
|
*/
|
|
20
74
|
export function opReturnScript(signalBytes) {
|
|
21
|
-
return Script.encode([
|
|
75
|
+
return Script.encode(['RETURN', signalBytes]);
|
|
22
76
|
}
|
|
23
77
|
/**
|
|
24
78
|
* Fetch the most recent confirmed UTXO at `bitcoinAddress` + the raw bytes of its
|
|
@@ -27,11 +81,11 @@ export function opReturnScript(signalBytes) {
|
|
|
27
81
|
async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
|
|
28
82
|
const utxos = await bitcoin.rest.address.getUtxos(bitcoinAddress);
|
|
29
83
|
if (!utxos.length) {
|
|
30
|
-
throw new BeaconError('No UTXOs found, please fund address!', 'UNFUNDED_BEACON_ADDRESS', { bitcoinAddress });
|
|
84
|
+
throw new BeaconError('No UTXOs found, please fund address!', 'UNFUNDED_BEACON_ADDRESS', { address: bitcoinAddress });
|
|
31
85
|
}
|
|
32
86
|
const utxo = utxos.sort((a, b) => b.status.block_height - a.status.block_height).shift();
|
|
33
87
|
if (!utxo) {
|
|
34
|
-
throw new BeaconError('Beacon bitcoin address unfunded or utxos unconfirmed.', 'UNFUNDED_BEACON_ADDRESS', { bitcoinAddress });
|
|
88
|
+
throw new BeaconError('Beacon bitcoin address unfunded or utxos unconfirmed.', 'UNFUNDED_BEACON_ADDRESS', { address: bitcoinAddress });
|
|
35
89
|
}
|
|
36
90
|
const prevTxHex = await bitcoin.rest.transaction.getHex(utxo.txid);
|
|
37
91
|
return { utxo, prevTxBytes: hexToBytes(prevTxHex) };
|
|
@@ -56,9 +110,12 @@ export async function buildAggregationBeaconTx(opts) {
|
|
|
56
110
|
// Fee cannot be probe-measured (no secret key for MuSig2 round). Use fixed P2TR vsize.
|
|
57
111
|
const feeSats = await feeEstimator.estimateFee(P2TR_BEACON_TX_VSIZE);
|
|
58
112
|
if (BigInt(utxo.value) <= feeSats) {
|
|
59
|
-
throw new BeaconError(`UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`, 'INSUFFICIENT_FUNDS', {
|
|
113
|
+
throw new BeaconError(`UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`, 'INSUFFICIENT_FUNDS', { address: opts.beaconAddress, valueSats: utxo.value, feeSats });
|
|
60
114
|
}
|
|
61
|
-
|
|
115
|
+
// allowUnknownOutputs: scure does not classify OP_RETURN as a "known" output
|
|
116
|
+
// type because it is unspendable by design. The opt-in flag tells scure we
|
|
117
|
+
// know the output is intentional (the beacon signal embedded in OP_RETURN).
|
|
118
|
+
const tx = new Transaction({ allowUnknownOutputs: true });
|
|
62
119
|
tx.addInput({
|
|
63
120
|
txid: utxo.txid,
|
|
64
121
|
index: utxo.vout,
|
|
@@ -75,8 +132,72 @@ export async function buildAggregationBeaconTx(opts) {
|
|
|
75
132
|
beaconAddress: opts.beaconAddress,
|
|
76
133
|
utxo,
|
|
77
134
|
feeSats,
|
|
135
|
+
scriptKind: 'p2tr',
|
|
78
136
|
};
|
|
79
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Sign the single input of a singleton beacon transaction. Dispatches to the
|
|
140
|
+
* correct sighash + signature-application path based on `kind`, finalizes the
|
|
141
|
+
* tx, and returns the signed raw hex.
|
|
142
|
+
*
|
|
143
|
+
* - **P2PKH**: legacy ECDSA sighash; scure assembles the scriptSig from `partialSig`.
|
|
144
|
+
* - **P2WPKH**: BIP-143 segwit-v0 sighash (P2PKH-shaped scriptCode); scure assembles
|
|
145
|
+
* the witness from `partialSig`.
|
|
146
|
+
* - **P2TR**: BIP-341 taproot key-path sighash (SIGHASH_DEFAULT); 64-byte BIP-340
|
|
147
|
+
* Schnorr signature applied via `tapKeySig`.
|
|
148
|
+
*/
|
|
149
|
+
async function signSingletonInput(tx, inputIdx, kind, signer, prevOutScript, amount) {
|
|
150
|
+
const pubkey = signer.publicKey;
|
|
151
|
+
if (kind === 'p2pkh') {
|
|
152
|
+
// Legacy sighash: scriptCode is the prev-output P2PKH script itself.
|
|
153
|
+
// scure-btc-signer marks `preimageLegacy` as TypeScript-private but does not
|
|
154
|
+
// expose a public alternative; its own `signIdx` consumes the secret key
|
|
155
|
+
// directly. We need only the sighash bytes so an external Signer can produce
|
|
156
|
+
// the signature, so we reach through the type system here. If scure ever
|
|
157
|
+
// renames this method, the P2PKH path tests fail loudly.
|
|
158
|
+
// TODO: track https://github.com/paulmillr/scure-btc-signer/issues/142 —
|
|
159
|
+
// drop the cast once a public preimage (e.g. `preimageP2PKH`) lands upstream.
|
|
160
|
+
const sighashType = SigHash.ALL;
|
|
161
|
+
const sighash = tx.preimageLegacy(inputIdx, prevOutScript, sighashType);
|
|
162
|
+
const sig = signer.sign(sighash, 'ecdsa');
|
|
163
|
+
const sigWithType = concatBytes(sig, new Uint8Array([sighashType]));
|
|
164
|
+
tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
|
|
165
|
+
tx.finalize();
|
|
166
|
+
return tx.hex;
|
|
167
|
+
}
|
|
168
|
+
if (kind === 'p2wpkh') {
|
|
169
|
+
// BIP-143: scriptCode for a P2WPKH input is the equivalent legacy P2PKH script
|
|
170
|
+
// (`OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG`). The P2PKH-shaped
|
|
171
|
+
// script appearing here in P2WPKH signing is intentional, not a bug.
|
|
172
|
+
//
|
|
173
|
+
// Derive the hash from `prevOutScript` (the bytes actually committed on-chain),
|
|
174
|
+
// not by re-hashing `signer.publicKey`. BIP-143 commits to the prev output, so
|
|
175
|
+
// the sighash must follow those bytes exactly. Rebuilding from the signer's
|
|
176
|
+
// pubkey assumes (rather than verifies) the two are in sync.
|
|
177
|
+
const decoded = OutScript.decode(prevOutScript);
|
|
178
|
+
if (decoded.type !== 'wpkh') {
|
|
179
|
+
throw new BeaconError(`Expected P2WPKH prev-output script, got "${decoded.type}".`, 'PREVOUT_SCRIPT_MISMATCH', { kind, observedScriptType: decoded.type });
|
|
180
|
+
}
|
|
181
|
+
const sighashScript = OutScript.encode({ type: 'pkh', hash: decoded.hash });
|
|
182
|
+
const sighashType = SigHash.ALL;
|
|
183
|
+
const sighash = tx.preimageWitnessV0(inputIdx, sighashScript, sighashType, amount);
|
|
184
|
+
const sig = signer.sign(sighash, 'ecdsa');
|
|
185
|
+
const sigWithType = concatBytes(sig, new Uint8Array([sighashType]));
|
|
186
|
+
tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
|
|
187
|
+
tx.finalize();
|
|
188
|
+
return tx.hex;
|
|
189
|
+
}
|
|
190
|
+
// P2TR key-path. BIP-341 requires signing with the taproot-tweaked secret
|
|
191
|
+
// `d' = taprootTweakPrivKey(d, merkleRoot)`; the verifier checks against the
|
|
192
|
+
// tweaked output internal key `Q = P + tG`. The tweak lives inside the Signer
|
|
193
|
+
// (it needs the secret key), so we use scheme 'bip341' rather than the raw
|
|
194
|
+
// 'bip340' scheme. No script tree on singleton beacons → no merkleRoot.
|
|
195
|
+
const sighash = tx.preimageWitnessV1(inputIdx, [prevOutScript], SigHash.DEFAULT, [amount]);
|
|
196
|
+
const sig = signer.sign(sighash, 'bip341');
|
|
197
|
+
tx.updateInput(inputIdx, { tapKeySig: sig });
|
|
198
|
+
tx.finalize();
|
|
199
|
+
return tx.hex;
|
|
200
|
+
}
|
|
80
201
|
/**
|
|
81
202
|
* Abstract base class for all BTCR2 Beacon types.
|
|
82
203
|
* A Beacon is a service listed in a BTCR2 DID document that informs resolvers
|
|
@@ -101,7 +222,9 @@ export class Beacon {
|
|
|
101
222
|
this.service = service;
|
|
102
223
|
}
|
|
103
224
|
/**
|
|
104
|
-
* Build + sign + broadcast a
|
|
225
|
+
* Build + sign + broadcast a singleton beacon signal transaction. The beacon
|
|
226
|
+
* address's script kind (P2PKH / P2WPKH / P2TR) is detected automatically
|
|
227
|
+
* and the input is constructed and signed accordingly.
|
|
105
228
|
*
|
|
106
229
|
* Composed from the three extracted phases ({@link buildSinglePartyTx},
|
|
107
230
|
* {@link signSinglePartyTx}, {@link broadcastRawTx}) so each piece can be exercised
|
|
@@ -110,72 +233,99 @@ export class Beacon {
|
|
|
110
233
|
* plumbing (UTXO fetch + OP_RETURN output + change output) is shared.
|
|
111
234
|
*
|
|
112
235
|
* @param signalBytes 32-byte payload to embed in OP_RETURN.
|
|
113
|
-
* @param
|
|
236
|
+
* @param signer Signer used to sign the spending input.
|
|
114
237
|
* @param bitcoin Bitcoin network connection.
|
|
115
238
|
* @param options Broadcast options (fee estimator, etc.).
|
|
116
239
|
* @returns The txid of the broadcast transaction.
|
|
117
240
|
* @throws {BeaconError} if the address is unfunded, no UTXO is available, or fee exceeds value.
|
|
118
241
|
*/
|
|
119
|
-
async buildSignAndBroadcast(signalBytes,
|
|
242
|
+
async buildSignAndBroadcast(signalBytes, signer, bitcoin, options) {
|
|
120
243
|
const feeEstimator = options?.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
121
244
|
const beaconAddress = this.service.serviceEndpoint.replace('bitcoin:', '');
|
|
122
245
|
const { utxo, prevTxBytes } = await fetchSpendableUtxo(beaconAddress, bitcoin);
|
|
123
246
|
const plan = await this.buildSinglePartyTx({
|
|
124
|
-
signalBytes, beaconAddress, utxo, prevTxBytes,
|
|
247
|
+
signalBytes, beaconAddress, utxo, prevTxBytes, signer, bitcoin, feeEstimator,
|
|
125
248
|
});
|
|
126
|
-
const signedHex = this.signSinglePartyTx(plan
|
|
249
|
+
const signedHex = await this.signSinglePartyTx(plan, signer);
|
|
127
250
|
return this.broadcastRawTx(bitcoin, signedHex);
|
|
128
251
|
}
|
|
129
252
|
/**
|
|
130
|
-
* Build an unsigned
|
|
131
|
-
* then rebuild with the real fee. Returns the tx and prev-output metadata.
|
|
253
|
+
* Build an unsigned singleton beacon tx ready for {@link signSinglePartyTx}.
|
|
132
254
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
255
|
+
* Detects the beacon address script kind (P2PKH / P2WPKH / P2TR) and configures
|
|
256
|
+
* the input accordingly. Validates that the signer's pubkey produces the beacon
|
|
257
|
+
* address under that script kind — without this check, a misconfigured caller
|
|
258
|
+
* would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
|
|
259
|
+
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant, avoiding any probe-sign
|
|
260
|
+
* round-trip.
|
|
135
261
|
*/
|
|
136
262
|
async buildSinglePartyTx(opts) {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
263
|
+
const network = opts.bitcoin.data;
|
|
264
|
+
const pubkey = opts.signer.publicKey;
|
|
265
|
+
const kind = detectSingletonScriptKind(opts.beaconAddress, network);
|
|
266
|
+
const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
|
|
267
|
+
if (derivedAddress !== opts.beaconAddress) {
|
|
268
|
+
throw new BeaconError(`Signer pubkey produces ${kind.toUpperCase()} address "${derivedAddress}", but beacon address is "${opts.beaconAddress}".`, 'SIGNER_KEY_MISMATCH', { kind, address: opts.beaconAddress, derivedAddress });
|
|
269
|
+
}
|
|
270
|
+
const feeSats = await opts.feeEstimator.estimateFee(SINGLETON_BEACON_TX_VSIZE[kind]);
|
|
271
|
+
const amount = BigInt(opts.utxo.value);
|
|
272
|
+
if (amount <= feeSats) {
|
|
273
|
+
throw new BeaconError(`UTXO value (${opts.utxo.value}) insufficient to cover fee (${feeSats}).`, 'INSUFFICIENT_FUNDS', { address: opts.beaconAddress, valueSats: opts.utxo.value, feeSats });
|
|
274
|
+
}
|
|
275
|
+
// allowUnknownOutputs: scure does not classify OP_RETURN as a "known" output
|
|
276
|
+
// type because it is unspendable by design. The opt-in flag tells scure we
|
|
277
|
+
// know the output is intentional (the beacon signal embedded in OP_RETURN).
|
|
278
|
+
const tx = new Transaction({ allowUnknownOutputs: true });
|
|
279
|
+
// Per-kind input setup: P2PKH consumes via nonWitnessUtxo only (legacy);
|
|
280
|
+
// P2WPKH and P2TR also carry a witnessUtxo (and P2TR carries tapInternalKey).
|
|
281
|
+
let prevOutScript;
|
|
282
|
+
if (kind === 'p2pkh') {
|
|
283
|
+
prevOutScript = p2pkh(pubkey, network).script;
|
|
142
284
|
tx.addInput({
|
|
143
285
|
txid: opts.utxo.txid,
|
|
144
286
|
index: opts.utxo.vout,
|
|
145
287
|
nonWitnessUtxo: opts.prevTxBytes,
|
|
146
|
-
witnessUtxo: { amount: BigInt(opts.utxo.value), script: witnessScript },
|
|
147
288
|
});
|
|
148
|
-
tx.addOutputAddress(opts.beaconAddress, BigInt(opts.utxo.value) - feeSats, opts.bitcoin.data);
|
|
149
|
-
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
150
|
-
return tx;
|
|
151
|
-
};
|
|
152
|
-
// First pass: sign with zero fee to measure vsize.
|
|
153
|
-
const probe = build(0n);
|
|
154
|
-
probe.signIdx(opts.secretKey, 0);
|
|
155
|
-
probe.finalize();
|
|
156
|
-
const vsize = probe.vsize;
|
|
157
|
-
const feeSats = await opts.feeEstimator.estimateFee(vsize);
|
|
158
|
-
if (BigInt(opts.utxo.value) <= feeSats) {
|
|
159
|
-
throw new BeaconError(`UTXO value (${opts.utxo.value}) insufficient to cover fee (${feeSats}).`, 'INSUFFICIENT_FUNDS', { bitcoinAddress: opts.beaconAddress, utxoValue: opts.utxo.value, fee: feeSats.toString() });
|
|
160
289
|
}
|
|
161
|
-
|
|
162
|
-
|
|
290
|
+
else if (kind === 'p2wpkh') {
|
|
291
|
+
prevOutScript = p2wpkh(pubkey, network).script;
|
|
292
|
+
tx.addInput({
|
|
293
|
+
txid: opts.utxo.txid,
|
|
294
|
+
index: opts.utxo.vout,
|
|
295
|
+
nonWitnessUtxo: opts.prevTxBytes,
|
|
296
|
+
witnessUtxo: { amount, script: prevOutScript },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// p2tr key-path
|
|
301
|
+
const internalKey = pubkey.slice(1, 33);
|
|
302
|
+
prevOutScript = p2tr(internalKey, undefined, network).script;
|
|
303
|
+
tx.addInput({
|
|
304
|
+
txid: opts.utxo.txid,
|
|
305
|
+
index: opts.utxo.vout,
|
|
306
|
+
nonWitnessUtxo: opts.prevTxBytes,
|
|
307
|
+
witnessUtxo: { amount, script: prevOutScript },
|
|
308
|
+
tapInternalKey: internalKey,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
tx.addOutputAddress(opts.beaconAddress, amount - feeSats, network);
|
|
312
|
+
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
163
313
|
return {
|
|
164
314
|
tx,
|
|
165
|
-
prevOutScripts: [
|
|
166
|
-
prevOutValues: [
|
|
315
|
+
prevOutScripts: [prevOutScript],
|
|
316
|
+
prevOutValues: [amount],
|
|
167
317
|
beaconAddress: opts.beaconAddress,
|
|
168
318
|
utxo: opts.utxo,
|
|
169
319
|
feeSats,
|
|
320
|
+
scriptKind: kind,
|
|
170
321
|
};
|
|
171
322
|
}
|
|
172
323
|
/**
|
|
173
324
|
* Sign + finalize the unsigned single-party tx and return its raw hex.
|
|
325
|
+
* Dispatches to the correct signing primitive based on `plan.scriptKind`.
|
|
174
326
|
*/
|
|
175
|
-
signSinglePartyTx(
|
|
176
|
-
tx.
|
|
177
|
-
tx.finalize();
|
|
178
|
-
return tx.hex;
|
|
327
|
+
async signSinglePartyTx(plan, signer) {
|
|
328
|
+
return signSingletonInput(plan.tx, 0, plan.scriptKind, signer, plan.prevOutScripts[0], plan.prevOutValues[0]);
|
|
179
329
|
}
|
|
180
330
|
/**
|
|
181
331
|
* Broadcast raw transaction hex via the Bitcoin REST endpoint. Returns the txid.
|
|
@@ -183,9 +333,5 @@ export class Beacon {
|
|
|
183
333
|
async broadcastRawTx(bitcoin, rawHex) {
|
|
184
334
|
return bitcoin.rest.transaction.send(rawHex);
|
|
185
335
|
}
|
|
186
|
-
/** Derive the compressed secp256k1 public key from a raw secret key. */
|
|
187
|
-
#derivePubkey(secretKey) {
|
|
188
|
-
return getPublicKey(secretKey, true);
|
|
189
|
-
}
|
|
190
336
|
}
|
|
191
337
|
//# sourceMappingURL=beacon.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"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"}
|
|
@@ -85,14 +85,14 @@ export class CASBeacon extends Beacon {
|
|
|
85
85
|
* and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
|
|
86
86
|
*
|
|
87
87
|
* @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
|
|
88
|
-
* @param {
|
|
88
|
+
* @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
|
|
89
89
|
* @param {BitcoinConnection} bitcoin The Bitcoin network connection.
|
|
90
90
|
* @param {CASBroadcastOptions} [options] Optional broadcast configuration, including a
|
|
91
91
|
* `casPublish` callback to publish the announcement off-chain and a `feeEstimator`.
|
|
92
92
|
* @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
|
|
93
93
|
* @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
|
|
94
94
|
*/
|
|
95
|
-
async broadcastSignal(signedUpdate,
|
|
95
|
+
async broadcastSignal(signedUpdate, signer, bitcoin, options) {
|
|
96
96
|
// Extract the DID from the beacon service id (strip the #fragment)
|
|
97
97
|
const did = this.service.id.split('#')[0];
|
|
98
98
|
// Hash the signed update (base64urlnopad for the CAS Announcement entry per spec)
|
|
@@ -102,7 +102,7 @@ export class CASBeacon extends Beacon {
|
|
|
102
102
|
// Canonicalize and hash the CAS Announcement for the OP_RETURN output
|
|
103
103
|
const announcementHash = hash(canonicalize(casAnnouncement));
|
|
104
104
|
// Delegate UTXO selection, PSBT construction, fee estimation, signing, and broadcast
|
|
105
|
-
await this.buildSignAndBroadcast(announcementHash,
|
|
105
|
+
await this.buildSignAndBroadcast(announcementHash, signer, bitcoin, options);
|
|
106
106
|
// Publish CAS Announcement to content-addressed store if callback provided
|
|
107
107
|
if (options?.casPublish) {
|
|
108
108
|
await options.casPublish(casAnnouncement);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cas-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/cas-beacon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cas-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/cas-beacon.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,15 +49,15 @@ export class SingletonBeacon extends Beacon {
|
|
|
49
49
|
* {@link Beacon.buildSignAndBroadcast}.
|
|
50
50
|
*
|
|
51
51
|
* @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
|
|
52
|
-
* @param {
|
|
52
|
+
* @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
|
|
53
53
|
* @param {BitcoinConnection} bitcoin The Bitcoin network connection.
|
|
54
54
|
* @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
|
|
55
55
|
* @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
|
|
56
56
|
* @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
|
|
57
57
|
*/
|
|
58
|
-
async broadcastSignal(signedUpdate,
|
|
58
|
+
async broadcastSignal(signedUpdate, signer, bitcoin, options) {
|
|
59
59
|
const signalBytes = hash(canonicalize(signedUpdate));
|
|
60
|
-
await this.buildSignAndBroadcast(signalBytes,
|
|
60
|
+
await this.buildSignAndBroadcast(signalBytes, signer, bitcoin, options);
|
|
61
61
|
return signedUpdate;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"singleton-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/singleton-beacon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"singleton-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/singleton-beacon.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAMvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,MAAM;IAEzC;;;OAGG;IACH,YAAY,OAAsB;QAChC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACH,cAAc,CACZ,OAA4B,EAC5B,OAAoB;QAEpB,MAAM,OAAO,GAAG,IAAI,KAAK,EAAsC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,KAAK,EAAY,CAAC;QAEpC,KAAI,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC5B,iEAAiE;YACjE,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;YAEtC,iDAAiD;YACjD,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAEvD,IAAG,CAAC,YAAY,EAAE,CAAC;gBACjB,uDAAuD;gBACvD,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI,EAAe,kBAAkB;oBACrC,UAAU;oBACV,eAAe,EAAI,IAAI,CAAC,OAAO,CAAC,EAAE;iBACnC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;IACD;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,eAAe,CACnB,YAA+B,EAC/B,MAAc,EACd,OAA0B,EAC1B,OAA0B;QAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACxE,OAAO,YAAY,CAAC;IACtB,CAAC;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalize } from '@did-btcr2/common';
|
|
2
|
-
import { blockHash, BTCR2MerkleTree, didToIndex,
|
|
2
|
+
import { base64UrlToHash, blockHash, BTCR2MerkleTree, didToIndex, hashToHex, verifySerializedProof } from '@did-btcr2/smt';
|
|
3
3
|
import { randomBytes } from '@noble/hashes/utils';
|
|
4
4
|
import { Beacon } from './beacon.js';
|
|
5
5
|
import { SMTBeaconError } from './error.js';
|
|
@@ -53,28 +53,36 @@ export class SMTBeacon extends Beacon {
|
|
|
53
53
|
});
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
|
-
//
|
|
57
|
-
if (!smtProof.updateId) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
// Nonce is required for proof verification
|
|
56
|
+
// Nonce is required for proof verification (inclusion and non-inclusion).
|
|
61
57
|
if (!smtProof.nonce) {
|
|
62
58
|
throw new SMTBeaconError('SMT proof missing required nonce field.', 'INVALID_SMT_PROOF', { smtProof, did });
|
|
63
59
|
}
|
|
64
|
-
// Verify
|
|
60
|
+
// Verify the SMT proof against the on-chain root. Leaf value per spec:
|
|
61
|
+
// inclusion = hash(hash(nonce) || updateId); non-inclusion = hash(hash(nonce)).
|
|
62
|
+
// Hash fields are base64url (no padding) per the SMT Proof spec. A
|
|
63
|
+
// non-inclusion proof (absent updateId) is verified too, not trusted.
|
|
65
64
|
const index = didToIndex(did);
|
|
66
|
-
const
|
|
65
|
+
const nonceHash = base64UrlToHash(smtProof.nonce);
|
|
66
|
+
const candidateHash = smtProof.updateId
|
|
67
|
+
? blockHash(blockHash(nonceHash), base64UrlToHash(smtProof.updateId))
|
|
68
|
+
: blockHash(blockHash(nonceHash));
|
|
67
69
|
const valid = verifySerializedProof(smtProof, index, candidateHash);
|
|
68
70
|
if (!valid) {
|
|
69
71
|
throw new SMTBeaconError('SMT proof verification failed.', 'INVALID_SMT_PROOF', { smtProof, did });
|
|
70
72
|
}
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
+
// Non-inclusion proof verified — no update for this DID this epoch, skip.
|
|
74
|
+
if (!smtProof.updateId) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Look up the signed update in sidecar updateMap (keyed by hex canonical
|
|
78
|
+
// hash). The proof's updateId is base64url, so convert to hex to match.
|
|
79
|
+
const updateHashHex = hashToHex(base64UrlToHash(smtProof.updateId));
|
|
80
|
+
const signedUpdate = sidecar.updateMap.get(updateHashHex);
|
|
73
81
|
if (!signedUpdate) {
|
|
74
82
|
// Signed update not available — emit a need
|
|
75
83
|
needs.push({
|
|
76
84
|
kind: 'NeedSignedUpdate',
|
|
77
|
-
updateHash:
|
|
85
|
+
updateHash: updateHashHex,
|
|
78
86
|
beaconServiceId: this.service.id
|
|
79
87
|
});
|
|
80
88
|
continue;
|
|
@@ -92,13 +100,13 @@ export class SMTBeacon extends Beacon {
|
|
|
92
100
|
* signing, and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
|
|
93
101
|
*
|
|
94
102
|
* @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
|
|
95
|
-
* @param {
|
|
103
|
+
* @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
|
|
96
104
|
* @param {BitcoinConnection} bitcoin The Bitcoin network connection.
|
|
97
105
|
* @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
|
|
98
106
|
* @return {Promise<SignedBTCR2Update>} The signed update that was broadcast.
|
|
99
107
|
* @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
|
|
100
108
|
*/
|
|
101
|
-
async broadcastSignal(signedUpdate,
|
|
109
|
+
async broadcastSignal(signedUpdate, signer, bitcoin, options) {
|
|
102
110
|
// Extract the DID from the beacon service id (strip the #fragment)
|
|
103
111
|
const did = this.service.id.split('#')[0];
|
|
104
112
|
// Build a single-entry SMT from the signed update
|
|
@@ -108,7 +116,7 @@ export class SMTBeacon extends Beacon {
|
|
|
108
116
|
tree.addEntries([{ did, nonce, signedUpdate: canonicalBytes }]);
|
|
109
117
|
tree.finalize();
|
|
110
118
|
// Root hash is the signal bytes for the OP_RETURN output
|
|
111
|
-
await this.buildSignAndBroadcast(tree.rootHash,
|
|
119
|
+
await this.buildSignAndBroadcast(tree.rootHash, signer, bitcoin, options);
|
|
112
120
|
return signedUpdate;
|
|
113
121
|
}
|
|
114
122
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smt-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/smt-beacon.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"smt-beacon.js","sourceRoot":"","sources":["../../../../src/core/beacon/smt-beacon.ts"],"names":[],"mappings":"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"}
|
|
@@ -149,11 +149,12 @@ export class Resolver {
|
|
|
149
149
|
for (const update of sidecar.casUpdates) {
|
|
150
150
|
casMap.set(canonicalHash(update, { encoding: 'hex' }), update);
|
|
151
151
|
}
|
|
152
|
-
// SMT Proofs map
|
|
152
|
+
// SMT Proofs map. proof.id is base64url per the SMT Proof spec; key by the
|
|
153
|
+
// hex root hash so lookups match the hex signalBytes from the OP_RETURN.
|
|
153
154
|
const smtMap = new Map();
|
|
154
155
|
if (sidecar.smtProofs?.length)
|
|
155
156
|
for (const proof of sidecar.smtProofs) {
|
|
156
|
-
smtMap.set(proof.id, proof);
|
|
157
|
+
smtMap.set(encodeHash(decodeHash(proof.id, 'base64urlnopad'), 'hex'), proof);
|
|
157
158
|
}
|
|
158
159
|
return { updateMap, casMap, smtMap };
|
|
159
160
|
}
|
|
@@ -479,8 +480,10 @@ export class Resolver {
|
|
|
479
480
|
case 'NeedSMTProof': {
|
|
480
481
|
const smtNeed = need;
|
|
481
482
|
const proof = data;
|
|
482
|
-
|
|
483
|
-
|
|
483
|
+
// proof.id is base64url per spec; smtRootHash is the hex on-chain signal.
|
|
484
|
+
const proofIdHex = encodeHash(decodeHash(proof.id, 'base64urlnopad'), 'hex');
|
|
485
|
+
if (proofIdHex !== smtNeed.smtRootHash) {
|
|
486
|
+
throw new ResolveError(`SMT proof root hash mismatch: expected ${smtNeed.smtRootHash}, got ${proofIdHex}`, INVALID_DID_UPDATE, { expected: smtNeed.smtRootHash, actual: proofIdHex });
|
|
484
487
|
}
|
|
485
488
|
this.#sidecarData.smtMap.set(smtNeed.smtRootHash, proof);
|
|
486
489
|
break;
|