@did-btcr2/method 0.48.0 → 0.50.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 +58 -72
- package/dist/browser.mjs +58 -72
- package/dist/cjs/index.js +61 -73
- package/dist/esm/core/beacon/beacon.js +74 -8
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/identifier.js +68 -130
- package/dist/esm/core/identifier.js.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +44 -0
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/identifier.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/beacon/beacon.ts +82 -12
- package/src/core/identifier.ts +77 -149
|
@@ -29,25 +29,30 @@ export class Identifier {
|
|
|
29
29
|
* @returns {string} The new did:btcr2 identifier.
|
|
30
30
|
*/
|
|
31
31
|
static encode(genesisBytes, options) {
|
|
32
|
-
// Deconstruct the options
|
|
33
|
-
const { idType, version = 1, network } = options;
|
|
34
|
-
//
|
|
32
|
+
// Deconstruct the options, defaulting version to 1 and network to "bitcoin" (matching DidBtcr2.create).
|
|
33
|
+
const { idType, version = 1, network = 'bitcoin' } = options;
|
|
34
|
+
// idType MUST be "KEY" or "EXTERNAL".
|
|
35
35
|
if (!(idType in IdentifierTypes)) {
|
|
36
36
|
throw new IdentifierError('Expected "idType" to be "KEY" or "EXTERNAL"', INVALID_DID, { idType });
|
|
37
37
|
}
|
|
38
|
-
//
|
|
39
|
-
|
|
38
|
+
// The only valid version_number is 1 (which encodes a btcr2_version of 0). Any other value would
|
|
39
|
+
// overflow or corrupt the version nibble, so reject everything except exactly 1. This also rejects
|
|
40
|
+
// NaN and non-number inputs, which are never strictly equal to 1.
|
|
41
|
+
if (version !== 1) {
|
|
40
42
|
throw new IdentifierError('Expected "version" to be 1', INVALID_DID, { version });
|
|
41
43
|
}
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// network MUST be a known network name. This encoder does not mint custom/numeric networks: the
|
|
45
|
+
// public surface (DidCreateOptions.network) is a string, and a numeric nibble >= 5 would overflow
|
|
46
|
+
// the 4-bit network field and corrupt the version nibble. Custom networks remain decode-only.
|
|
47
|
+
if (typeof network !== 'string') {
|
|
48
|
+
throw new IdentifierError('Expected "network" to be a known network name', INVALID_DID, { network });
|
|
45
49
|
}
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
throw new IdentifierError('Invalid "network"
|
|
50
|
+
const networkValue = BitcoinNetworkNames[network];
|
|
51
|
+
if (networkValue === undefined) {
|
|
52
|
+
throw new IdentifierError('Invalid "network" name', INVALID_DID, { network });
|
|
49
53
|
}
|
|
50
|
-
//
|
|
54
|
+
// genesisBytes MUST match the identifier type: a valid compressed secp256k1 public key for KEY,
|
|
55
|
+
// or a 32-byte hash for EXTERNAL. An EXTERNAL DID minted with any other length is unresolvable.
|
|
51
56
|
if (idType === 'KEY') {
|
|
52
57
|
try {
|
|
53
58
|
new CompressedSecp256k1PublicKey(genesisBytes);
|
|
@@ -56,47 +61,15 @@ export class Identifier {
|
|
|
56
61
|
throw new IdentifierError('Expected "genesisBytes" to be a valid compressed secp256k1 public key', INVALID_DID, { genesisBytes });
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// 6.2 “external” - “x”
|
|
62
|
-
const hrp = idType === 'KEY' ? 'k' : 'x';
|
|
63
|
-
// 7. Create an empty nibbles numeric array.
|
|
64
|
-
const nibbles = [];
|
|
65
|
-
// 8. Set fCount equal to (version - 1) / 15, rounded down.
|
|
66
|
-
const fCount = Math.floor((version - 1) / 15);
|
|
67
|
-
// 9. Append hexadecimal F (decimal 15) to nibbles fCount times.
|
|
68
|
-
for (let i = 0; i < fCount; i++) {
|
|
69
|
-
nibbles.push(15);
|
|
70
|
-
}
|
|
71
|
-
// 10. Append (version - 1) mod 15 to nibbles.
|
|
72
|
-
nibbles.push((version - 1) % 15);
|
|
73
|
-
// 11. If network is a string, append the numeric value from the following map to nibbles:
|
|
74
|
-
// "bitcoin" - 0
|
|
75
|
-
// "signet" - 1
|
|
76
|
-
// "regtest" - 2
|
|
77
|
-
// "testnet3" - 3
|
|
78
|
-
// "testnet4" - 4
|
|
79
|
-
// "mutinynet" - 5
|
|
80
|
-
if (typeof network === 'string') {
|
|
81
|
-
nibbles.push(BitcoinNetworkNames[network]);
|
|
82
|
-
}
|
|
83
|
-
else if (typeof network === 'number') {
|
|
84
|
-
// 12. If network is a number, append network + 11 to nibbles.
|
|
85
|
-
nibbles.push(network + 11);
|
|
64
|
+
else if (genesisBytes.length !== 32) {
|
|
65
|
+
throw new IdentifierError('Expected "genesisBytes" to be a 32-byte hash for EXTERNAL identifiers', INVALID_DID, { genesisBytes });
|
|
86
66
|
}
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (fCount !== 0) {
|
|
94
|
-
for (const index in Array.from({ length: (nibbles.length / 2) - 1 })) {
|
|
95
|
-
throw new IdentifierError('Not implemented', 'NOT_IMPLEMENTED', { index });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const dataBytes = new Uint8Array([(nibbles[2 * 0] << 4) | nibbles[2 * 0 + 1], ...genesisBytes]);
|
|
99
|
-
// 18. Return identifier.
|
|
67
|
+
// Map idType to its human-readable part: KEY -> "k", EXTERNAL -> "x".
|
|
68
|
+
const hrp = idType === 'KEY' ? 'k' : 'x';
|
|
69
|
+
// Pack btcr2_version (high nibble, = version - 1 = 0) and network_value (low nibble) into the first
|
|
70
|
+
// byte, then append genesisBytes. Bech32m-encode the result.
|
|
71
|
+
const firstByte = ((version - 1) << 4) | networkValue;
|
|
72
|
+
const dataBytes = new Uint8Array([firstByte, ...genesisBytes]);
|
|
100
73
|
return `did:btcr2:${bech32m.encodeFromBytes(hrp, dataBytes)}`;
|
|
101
74
|
}
|
|
102
75
|
/**
|
|
@@ -108,104 +81,66 @@ export class Identifier {
|
|
|
108
81
|
* @throws {DidErrorCode.MethodNotSupported} if the method is not supported
|
|
109
82
|
*/
|
|
110
83
|
static decode(identifier) {
|
|
111
|
-
// 1. Split identifier into
|
|
84
|
+
// 1. Split the identifier into scheme, method, and encoded id at the colon character.
|
|
112
85
|
const components = identifier.split(':');
|
|
113
|
-
// 2.
|
|
86
|
+
// 2. There MUST be exactly three colon-separated components.
|
|
114
87
|
if (components.length !== 3) {
|
|
115
88
|
throw new IdentifierError(`Invalid did: ${identifier}`, INVALID_DID, { identifier });
|
|
116
89
|
}
|
|
117
|
-
// Deconstruct the components of the identifier: scheme, method, encoded
|
|
118
90
|
const [scheme, method, encoded] = components;
|
|
119
|
-
// 3.
|
|
91
|
+
// 3. The scheme MUST be "did".
|
|
120
92
|
if (scheme !== 'did') {
|
|
121
93
|
throw new IdentifierError(`Invalid did: ${identifier}`, INVALID_DID, { identifier });
|
|
122
94
|
}
|
|
123
|
-
// 4.
|
|
95
|
+
// 4. The method MUST be "btcr2".
|
|
124
96
|
if (method !== 'btcr2') {
|
|
125
97
|
throw new IdentifierError(`Invalid did method: ${method}`, METHOD_NOT_SUPPORTED, { identifier });
|
|
126
98
|
}
|
|
127
|
-
// 5.
|
|
99
|
+
// 5. The method-specific id MUST be present.
|
|
128
100
|
if (!encoded) {
|
|
129
101
|
throw new IdentifierError(`Invalid method-specific id: ${identifier}`, INVALID_DID, { identifier });
|
|
130
102
|
}
|
|
131
|
-
// 6.
|
|
103
|
+
// 6. Bech32m-decode the id into its hrp and dataBytes.
|
|
132
104
|
const { prefix: hrp, bytes: dataBytes } = bech32m.decodeToBytes(encoded);
|
|
133
|
-
// 7.
|
|
105
|
+
// 7. The hrp MUST be "k" (KEY) or "x" (EXTERNAL).
|
|
134
106
|
if (!['x', 'k'].includes(hrp)) {
|
|
135
107
|
throw new IdentifierError(`Invalid hrp: ${hrp}`, INVALID_DID, { identifier });
|
|
136
108
|
}
|
|
137
|
-
|
|
109
|
+
// 8. There MUST be at least one byte to read btcr2_version and network_value from.
|
|
110
|
+
if (!dataBytes || dataBytes.length < 1) {
|
|
138
111
|
throw new IdentifierError(`Failed to decode id: ${encoded}`, INVALID_DID, { identifier });
|
|
139
112
|
}
|
|
140
|
-
//
|
|
141
|
-
// “k” - “key”
|
|
142
|
-
// “x” - “external”
|
|
143
|
-
// other - raise invalidDid error
|
|
113
|
+
// 9. Map hrp to idType.
|
|
144
114
|
const idType = hrp === 'k' ? 'KEY' : 'EXTERNAL';
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// 15. Advance to the next nibble and set networkValue to its value.
|
|
175
|
-
let networkValue = nibblesConsumed % 2 === 0
|
|
176
|
-
? dataBytes[++byteIndex] >>> 4
|
|
177
|
-
: currentByte & 0x0F;
|
|
178
|
-
nibblesConsumed += 1;
|
|
179
|
-
// 16. Map networkValue to network from the following:
|
|
180
|
-
// 0 - "bitcoin"
|
|
181
|
-
// 1 - "signet"
|
|
182
|
-
// 2 - "regtest"
|
|
183
|
-
// 3 - "testnet3"
|
|
184
|
-
// 4 - "testnet4"
|
|
185
|
-
// 5 - "mutinynet"
|
|
186
|
-
// 6-7 - raise invalidDid error
|
|
187
|
-
// 8-F - networkValue - 11
|
|
188
|
-
let network = BitcoinNetworkNames[networkValue];
|
|
189
|
-
if (!network) {
|
|
190
|
-
if (networkValue >= 0x8 && networkValue <= 0xF) {
|
|
191
|
-
network = networkValue - 11;
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
throw new IdentifierError(`Invalid did: ${identifier}`, INVALID_DID, { identifier });
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
// 17. If the number of nibbles consumed is odd:
|
|
198
|
-
if (nibblesConsumed % 2 === 1) {
|
|
199
|
-
// 17.1 Advance to the next nibble and set fillerNibble to its value.
|
|
200
|
-
const fillerNibble = currentByte & 0x0F;
|
|
201
|
-
// 17.2 If fillerNibble is not 0, raise invalidDid error.
|
|
202
|
-
if (fillerNibble !== 0) {
|
|
203
|
-
throw new IdentifierError(`Invalid did: ${identifier}`, INVALID_DID, { identifier });
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// 18. Set genesisBytes to the remaining dataBytes.
|
|
207
|
-
const genesisBytes = dataBytes.slice(byteIndex + 1);
|
|
208
|
-
// 19. If idType is “key” and genesisBytes is not a valid compressed secp256k1 public key, raise invalidDid error.
|
|
115
|
+
// 10. btcr2_version is the high nibble of the first byte and MUST be 0, which is version_number 1.
|
|
116
|
+
// The version-extension scheme (a leading nibble of 0xF chaining into further bytes) is reserved
|
|
117
|
+
// and not valid under v1, so any non-zero high nibble (0x1 through 0xF) is a malformed or forged
|
|
118
|
+
// identifier and is rejected here. Reading a single flat nibble (rather than looping on 0xF) is
|
|
119
|
+
// what makes this guard actually fire: the previous loop body never ran for a leading nibble of
|
|
120
|
+
// 0x1 through 0xE, silently accepting forged versions.
|
|
121
|
+
const btcr2Version = dataBytes[0] >>> 4;
|
|
122
|
+
if (btcr2Version !== 0) {
|
|
123
|
+
throw new IdentifierError(`Invalid btcr2_version (expected 0): ${btcr2Version}`, INVALID_DID, { identifier });
|
|
124
|
+
}
|
|
125
|
+
const version = 1;
|
|
126
|
+
// 11. network_value is the low nibble of the first byte. 0-5 map to named networks; 12-14 are custom
|
|
127
|
+
// networks (returned as the numeric values 1-3); 6-11 and 15 are reserved/out-of-range and rejected.
|
|
128
|
+
const networkValue = dataBytes[0] & 0x0F;
|
|
129
|
+
const networkName = BitcoinNetworkNames[networkValue];
|
|
130
|
+
let network;
|
|
131
|
+
if (typeof networkName === 'string') {
|
|
132
|
+
network = networkName;
|
|
133
|
+
}
|
|
134
|
+
else if (networkValue >= 12 && networkValue <= 14) {
|
|
135
|
+
network = networkValue - 11;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw new IdentifierError(`Invalid network: ${networkValue}`, INVALID_DID, { identifier });
|
|
139
|
+
}
|
|
140
|
+
// 12. genesisBytes is everything after the first byte.
|
|
141
|
+
const genesisBytes = dataBytes.slice(1);
|
|
142
|
+
// 13. genesisBytes MUST match the identifier type: a valid compressed secp256k1 public key for KEY,
|
|
143
|
+
// or a 32-byte hash for EXTERNAL.
|
|
209
144
|
if (idType === 'KEY') {
|
|
210
145
|
try {
|
|
211
146
|
new CompressedSecp256k1PublicKey(genesisBytes);
|
|
@@ -214,7 +149,10 @@ export class Identifier {
|
|
|
214
149
|
throw new IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, INVALID_DID, { identifier });
|
|
215
150
|
}
|
|
216
151
|
}
|
|
217
|
-
|
|
152
|
+
else if (genesisBytes.length !== 32) {
|
|
153
|
+
throw new IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, INVALID_DID, { identifier });
|
|
154
|
+
}
|
|
155
|
+
// 14. Return idType, hrp, version, network, and genesisBytes.
|
|
218
156
|
return { idType, hrp, version, network, genesisBytes };
|
|
219
157
|
}
|
|
220
158
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../src/core/identifier.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC7H,OAAO,EAAE,4BAA4B,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAWrC,CAAC;AAgBF;;;;;;;;;;GAUG;AACH,MAAM,OAAO,UAAU;IACrB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,MAAM,CAAC,YAAsC,EAAE,OAAyB;QAC7E,
|
|
1
|
+
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../src/core/identifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -138,6 +138,50 @@ export interface BeaconTxPlan {
|
|
|
138
138
|
* non-standard and rejected at broadcast with `RPC error -26: scriptpubkey`.
|
|
139
139
|
*/
|
|
140
140
|
export declare function opReturnScript(signalBytes: Uint8Array): Uint8Array;
|
|
141
|
+
/**
|
|
142
|
+
* Minimum value (sats) a beacon UTXO must exceed for {@link selectSpendableUtxo} to
|
|
143
|
+
* treat it as spendable. This is a fixed, conservative, script-kind-agnostic floor:
|
|
144
|
+
* an output at or below it is too small to be worth spending, so selection discards
|
|
145
|
+
* it in favor of a larger confirmed UTXO. Keeping the floor a constant (rather than
|
|
146
|
+
* deriving it from a fee estimate) keeps selection pure and fee-estimator-independent.
|
|
147
|
+
*
|
|
148
|
+
* The floor is a coarse pre-filter, not the fee-coverage boundary: whether a selected
|
|
149
|
+
* UTXO actually covers the transaction fee is a separate check, enforced against the
|
|
150
|
+
* live {@link FeeEstimator} by the builders' `value <= feeSats` guard
|
|
151
|
+
* ({@link SinglePartyBeacon.buildSinglePartyTx} and {@link buildAggregationBeaconTx}).
|
|
152
|
+
* At the default 5 sat/vB rate that fee (roughly 775 to 1200 sats across the three
|
|
153
|
+
* script kinds) sits above this floor, so a UTXO can clear the dust filter and still
|
|
154
|
+
* be rejected as insufficient; conversely, at a very low fee rate an output near the
|
|
155
|
+
* floor could cover the fee. The floor's job is only to skip trivially small inputs.
|
|
156
|
+
*
|
|
157
|
+
* The value is the standard Bitcoin Core P2PKH dust threshold, the largest of the
|
|
158
|
+
* three singleton beacon script kinds (P2PKH 546, P2TR 330, P2WPKH 294 per
|
|
159
|
+
* {@link DUST_LIMIT_SATS}): a UTXO above it is non-dust under any beacon address kind.
|
|
160
|
+
* Distinct from {@link DUST_LIMIT_SATS}, which sizes the outgoing change output by
|
|
161
|
+
* kind; this bounds the incoming UTXO chosen to fund the transaction.
|
|
162
|
+
*/
|
|
163
|
+
export declare const SPENDABLE_DUST_LIMIT_SATS = 546;
|
|
164
|
+
/**
|
|
165
|
+
* Select the beacon UTXO to fund a signal transaction from the set of UTXOs at a
|
|
166
|
+
* beacon address. Pure and deterministic: the same address state always yields the
|
|
167
|
+
* same input, across broadcast retries and across independent resolvers.
|
|
168
|
+
*
|
|
169
|
+
* Filters to confirmed UTXOs (an unconfirmed input is reorg- and RBF-unsafe: a
|
|
170
|
+
* signal built on it can be orphaned or double-spent before it confirms), then drops
|
|
171
|
+
* dust at or below {@link SPENDABLE_DUST_LIMIT_SATS}, then picks the deepest via
|
|
172
|
+
* {@link byDepthThenId}. The did:btcr2 spec does not mandate a selection rule or a
|
|
173
|
+
* confirmation depth (only the security considerations favor deeper confirmations),
|
|
174
|
+
* so this is an implementation policy: prefer safety and reproducibility over
|
|
175
|
+
* spending the newest or largest output.
|
|
176
|
+
*
|
|
177
|
+
* @param utxos UTXOs reported at the beacon address.
|
|
178
|
+
* @param address Beacon address, used only to annotate thrown errors.
|
|
179
|
+
* @returns The confirmed, non-dust, deepest UTXO.
|
|
180
|
+
* @throws {BeaconError} `UNFUNDED_BEACON_ADDRESS` when no UTXOs exist at all;
|
|
181
|
+
* `NO_SPENDABLE_BEACON_UTXO` when UTXOs exist but none are both confirmed and above
|
|
182
|
+
* the dust limit (the message distinguishes all-unconfirmed from all-dust).
|
|
183
|
+
*/
|
|
184
|
+
export declare function selectSpendableUtxo(utxos: Array<AddressUtxo>, address?: string): AddressUtxo;
|
|
141
185
|
/**
|
|
142
186
|
* Build an aggregation beacon transaction (P2TR key-path spend) ready for MuSig2 signing.
|
|
143
187
|
* Returns the unsigned Transaction + prev-output metadata that an aggregation service's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAA4D,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAInF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAI9E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAIzE,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,mBAAmB,EAC/B,UAAU,EAAE,mBAAmB,GAC9B,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,UAAU,GAClB,mBAAmB,CAWrB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,UAAU,GAClB,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,UAAU,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,CAYR;AAeD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kGAAkG;IAClG,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,EAAE,EAAE,WAAW,CAAC;IAChB,6EAA6E;IAC7E,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,uDAAuD;IACvD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,uGAAuG;IACvG,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,CAElE;
|
|
1
|
+
{"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../../src/core/identifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EAAE,4BAA4B,EAAkB,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACvD,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;CACvB;AACD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,GAAG,aAAa,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../../src/core/identifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EAAE,4BAA4B,EAAkB,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACvD,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;CACvB;AACD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,GAAG,aAAa,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM;IAuDxF;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAsFhD;;;OAGG;IACH,MAAM,CAAC,QAAQ,IAAI;QAAE,OAAO,EAAE,oBAAoB,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAYjE;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,4BAA4B;IAW9D;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;CAQ5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@did-btcr2/method",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.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",
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"canonicalize": "^2.1.0",
|
|
78
78
|
"dotenv": "^16.6.1",
|
|
79
79
|
"nostr-tools": "^2.23.3",
|
|
80
|
-
"@did-btcr2/bitcoin": "^0.8.0",
|
|
81
|
-
"@did-btcr2/smt": "^0.3.0",
|
|
82
80
|
"@did-btcr2/common": "^9.1.0",
|
|
83
81
|
"@did-btcr2/keypair": "^0.13.1",
|
|
84
|
-
"@did-btcr2/
|
|
82
|
+
"@did-btcr2/smt": "^0.3.0",
|
|
83
|
+
"@did-btcr2/cryptosuite": "^9.0.0",
|
|
84
|
+
"@did-btcr2/bitcoin": "^0.8.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@eslint/js": "^9.39.4",
|
|
@@ -229,27 +229,97 @@ export function opReturnScript(signalBytes: Uint8Array): Uint8Array {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
232
|
+
* Minimum value (sats) a beacon UTXO must exceed for {@link selectSpendableUtxo} to
|
|
233
|
+
* treat it as spendable. This is a fixed, conservative, script-kind-agnostic floor:
|
|
234
|
+
* an output at or below it is too small to be worth spending, so selection discards
|
|
235
|
+
* it in favor of a larger confirmed UTXO. Keeping the floor a constant (rather than
|
|
236
|
+
* deriving it from a fee estimate) keeps selection pure and fee-estimator-independent.
|
|
237
|
+
*
|
|
238
|
+
* The floor is a coarse pre-filter, not the fee-coverage boundary: whether a selected
|
|
239
|
+
* UTXO actually covers the transaction fee is a separate check, enforced against the
|
|
240
|
+
* live {@link FeeEstimator} by the builders' `value <= feeSats` guard
|
|
241
|
+
* ({@link SinglePartyBeacon.buildSinglePartyTx} and {@link buildAggregationBeaconTx}).
|
|
242
|
+
* At the default 5 sat/vB rate that fee (roughly 775 to 1200 sats across the three
|
|
243
|
+
* script kinds) sits above this floor, so a UTXO can clear the dust filter and still
|
|
244
|
+
* be rejected as insufficient; conversely, at a very low fee rate an output near the
|
|
245
|
+
* floor could cover the fee. The floor's job is only to skip trivially small inputs.
|
|
246
|
+
*
|
|
247
|
+
* The value is the standard Bitcoin Core P2PKH dust threshold, the largest of the
|
|
248
|
+
* three singleton beacon script kinds (P2PKH 546, P2TR 330, P2WPKH 294 per
|
|
249
|
+
* {@link DUST_LIMIT_SATS}): a UTXO above it is non-dust under any beacon address kind.
|
|
250
|
+
* Distinct from {@link DUST_LIMIT_SATS}, which sizes the outgoing change output by
|
|
251
|
+
* kind; this bounds the incoming UTXO chosen to fund the transaction.
|
|
234
252
|
*/
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
export const SPENDABLE_DUST_LIMIT_SATS = 546;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Deterministic ordering for spendable UTXO selection: deepest first (ascending
|
|
257
|
+
* block height, so the most-confirmed UTXO sorts first), tie-broken by `txid` then
|
|
258
|
+
* `vout`. The tie-break makes the winner independent of the order the REST API
|
|
259
|
+
* returns UTXOs in, so retries and independent resolvers converge on the same input.
|
|
260
|
+
*/
|
|
261
|
+
function byDepthThenId(a: AddressUtxo, b: AddressUtxo): number {
|
|
262
|
+
if(a.status.block_height !== b.status.block_height) {
|
|
263
|
+
return a.status.block_height - b.status.block_height;
|
|
264
|
+
}
|
|
265
|
+
const txidOrder = a.txid.localeCompare(b.txid);
|
|
266
|
+
return txidOrder !== 0 ? txidOrder : a.vout - b.vout;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Select the beacon UTXO to fund a signal transaction from the set of UTXOs at a
|
|
271
|
+
* beacon address. Pure and deterministic: the same address state always yields the
|
|
272
|
+
* same input, across broadcast retries and across independent resolvers.
|
|
273
|
+
*
|
|
274
|
+
* Filters to confirmed UTXOs (an unconfirmed input is reorg- and RBF-unsafe: a
|
|
275
|
+
* signal built on it can be orphaned or double-spent before it confirms), then drops
|
|
276
|
+
* dust at or below {@link SPENDABLE_DUST_LIMIT_SATS}, then picks the deepest via
|
|
277
|
+
* {@link byDepthThenId}. The did:btcr2 spec does not mandate a selection rule or a
|
|
278
|
+
* confirmation depth (only the security considerations favor deeper confirmations),
|
|
279
|
+
* so this is an implementation policy: prefer safety and reproducibility over
|
|
280
|
+
* spending the newest or largest output.
|
|
281
|
+
*
|
|
282
|
+
* @param utxos UTXOs reported at the beacon address.
|
|
283
|
+
* @param address Beacon address, used only to annotate thrown errors.
|
|
284
|
+
* @returns The confirmed, non-dust, deepest UTXO.
|
|
285
|
+
* @throws {BeaconError} `UNFUNDED_BEACON_ADDRESS` when no UTXOs exist at all;
|
|
286
|
+
* `NO_SPENDABLE_BEACON_UTXO` when UTXOs exist but none are both confirmed and above
|
|
287
|
+
* the dust limit (the message distinguishes all-unconfirmed from all-dust).
|
|
288
|
+
*/
|
|
289
|
+
export function selectSpendableUtxo(utxos: Array<AddressUtxo>, address?: string): AddressUtxo {
|
|
240
290
|
if(!utxos.length) {
|
|
241
291
|
throw new BeaconError(
|
|
242
292
|
'No UTXOs found, please fund address!',
|
|
243
|
-
'UNFUNDED_BEACON_ADDRESS', { address
|
|
293
|
+
'UNFUNDED_BEACON_ADDRESS', { address }
|
|
244
294
|
);
|
|
245
295
|
}
|
|
246
|
-
const
|
|
247
|
-
|
|
296
|
+
const confirmed = utxos.filter(utxo => utxo.status.confirmed === true);
|
|
297
|
+
const spendable = confirmed.filter(utxo => utxo.value > SPENDABLE_DUST_LIMIT_SATS);
|
|
298
|
+
if(!spendable.length) {
|
|
299
|
+
const reason = confirmed.length === 0
|
|
300
|
+
? `all ${utxos.length} UTXO(s) are unconfirmed`
|
|
301
|
+
: `all ${confirmed.length} confirmed UTXO(s) are at or below the ${SPENDABLE_DUST_LIMIT_SATS}-sat dust limit`;
|
|
248
302
|
throw new BeaconError(
|
|
249
|
-
|
|
250
|
-
'
|
|
303
|
+
`No spendable UTXO at beacon address: ${reason}.`,
|
|
304
|
+
'NO_SPENDABLE_BEACON_UTXO',
|
|
305
|
+
{ address, total: utxos.length, confirmed: confirmed.length, dustLimit: SPENDABLE_DUST_LIMIT_SATS }
|
|
251
306
|
);
|
|
252
307
|
}
|
|
308
|
+
return [ ...spendable ].sort(byDepthThenId)[0]!;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Fetch the deepest confirmed, non-dust spendable UTXO at `bitcoinAddress` plus the
|
|
313
|
+
* raw bytes of its parent transaction (needed by PSBT inputs). Selection is delegated
|
|
314
|
+
* to {@link selectSpendableUtxo}; throws {@link BeaconError} when the address is
|
|
315
|
+
* unfunded or has no confirmed, non-dust UTXO.
|
|
316
|
+
*/
|
|
317
|
+
async function fetchSpendableUtxo(
|
|
318
|
+
bitcoinAddress: string,
|
|
319
|
+
bitcoin: BitcoinConnection,
|
|
320
|
+
): Promise<{ utxo: AddressUtxo; prevTxBytes: Uint8Array }> {
|
|
321
|
+
const utxos = await bitcoin.rest.address.getUtxos(bitcoinAddress);
|
|
322
|
+
const utxo = selectSpendableUtxo(utxos, bitcoinAddress);
|
|
253
323
|
const prevTxHex = await bitcoin.rest.transaction.getHex(utxo.txid);
|
|
254
324
|
return { utxo, prevTxBytes: hexToBytes(prevTxHex) };
|
|
255
325
|
}
|