@did-btcr2/method 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +89 -30
- package/dist/browser.mjs +89 -30
- package/dist/cjs/index.js +91 -29
- package/dist/esm/core/aggregation/runner/aggregation-runner.js +1 -0
- package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +7 -2
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
- package/dist/esm/core/beacon/beacon.js +101 -10
- package/dist/esm/core/beacon/beacon.js.map +1 -1
- package/dist/esm/core/beacon/fee-estimator.js +8 -0
- package/dist/esm/core/beacon/fee-estimator.js.map +1 -1
- package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +6 -0
- package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts +19 -2
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +59 -3
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/fee-estimator.d.ts +8 -0
- package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/core/aggregation/runner/aggregation-runner.ts +7 -0
- package/src/core/aggregation/runner/service-runner.ts +24 -2
- package/src/core/beacon/beacon.ts +131 -12
- package/src/core/beacon/fee-estimator.ts +9 -0
package/dist/browser.js
CHANGED
|
@@ -93172,6 +93172,7 @@ a=end-of-candidates
|
|
|
93172
93172
|
BeaconUtils: () => BeaconUtils,
|
|
93173
93173
|
CASBeacon: () => CASBeacon,
|
|
93174
93174
|
CASBeaconError: () => CASBeaconError,
|
|
93175
|
+
CHANGE_OUTPUT_VBYTES: () => CHANGE_OUTPUT_VBYTES,
|
|
93175
93176
|
COHORT_ADVERT: () => COHORT_ADVERT,
|
|
93176
93177
|
COHORT_OPT_IN: () => COHORT_OPT_IN,
|
|
93177
93178
|
COHORT_OPT_IN_ACCEPT: () => COHORT_OPT_IN_ACCEPT,
|
|
@@ -93180,6 +93181,7 @@ a=end-of-candidates
|
|
|
93180
93181
|
DEFAULT_ADVERT_REPEAT_INTERVAL_MS: () => DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
|
|
93181
93182
|
DEFAULT_BROADCAST_LOOKBACK_MS: () => DEFAULT_BROADCAST_LOOKBACK_MS,
|
|
93182
93183
|
DEFAULT_CLOCK_SKEW_SEC: () => DEFAULT_CLOCK_SKEW_SEC,
|
|
93184
|
+
DEFAULT_FEE_ESTIMATOR: () => DEFAULT_FEE_ESTIMATOR,
|
|
93183
93185
|
DEFAULT_FUNDING_MODEL: () => DEFAULT_FUNDING_MODEL,
|
|
93184
93186
|
DEFAULT_MAX_UPDATE_SIZE_BYTES: () => DEFAULT_MAX_UPDATE_SIZE_BYTES,
|
|
93185
93187
|
DEFAULT_NONCE_LEN_BYTES: () => DEFAULT_NONCE_LEN_BYTES,
|
|
@@ -93187,6 +93189,7 @@ a=end-of-candidates
|
|
|
93187
93189
|
DEFAULT_RECOVERY_SEQUENCE: () => DEFAULT_RECOVERY_SEQUENCE,
|
|
93188
93190
|
DID_REGEX: () => DID_REGEX,
|
|
93189
93191
|
DISTRIBUTE_AGGREGATED_DATA: () => DISTRIBUTE_AGGREGATED_DATA,
|
|
93192
|
+
DUST_LIMIT_SATS: () => DUST_LIMIT_SATS2,
|
|
93190
93193
|
DidBtcr2: () => DidBtcr2,
|
|
93191
93194
|
DidCommTransport: () => DidCommTransport,
|
|
93192
93195
|
DidDocument: () => DidDocument,
|
|
@@ -93243,6 +93246,7 @@ a=end-of-candidates
|
|
|
93243
93246
|
TypedEventEmitter: () => TypedEventEmitter,
|
|
93244
93247
|
Updater: () => Updater,
|
|
93245
93248
|
VALIDATION_ACK: () => VALIDATION_ACK,
|
|
93249
|
+
beaconTxVsize: () => beaconTxVsize,
|
|
93246
93250
|
buildAggregationBeaconTx: () => buildAggregationBeaconTx,
|
|
93247
93251
|
buildFallbackLeaf: () => buildFallbackLeaf,
|
|
93248
93252
|
buildFallbackSpend: () => buildFallbackSpend,
|
|
@@ -93294,6 +93298,7 @@ a=end-of-candidates
|
|
|
93294
93298
|
parseRequestAuth: () => parseRequestAuth,
|
|
93295
93299
|
parseSseStream: () => parseSseStream,
|
|
93296
93300
|
registerBeaconStrategy: () => registerBeaconStrategy,
|
|
93301
|
+
resolveChangeAddress: () => resolveChangeAddress,
|
|
93297
93302
|
resolveFallbackThreshold: () => resolveFallbackThreshold,
|
|
93298
93303
|
reviveFromWire: () => reviveFromWire,
|
|
93299
93304
|
signEnvelope: () => signEnvelope,
|
|
@@ -112455,6 +112460,30 @@ ${value2}`;
|
|
|
112455
112460
|
|
|
112456
112461
|
// src/core/aggregation/runner/service-runner.ts
|
|
112457
112462
|
init_shim();
|
|
112463
|
+
|
|
112464
|
+
// src/core/beacon/fee-estimator.ts
|
|
112465
|
+
init_shim();
|
|
112466
|
+
var StaticFeeEstimator = class {
|
|
112467
|
+
satsPerVbyte;
|
|
112468
|
+
/**
|
|
112469
|
+
* @param satsPerVbyte Fee rate in satoshis per virtual byte. Default: 5 sat/vB.
|
|
112470
|
+
*/
|
|
112471
|
+
constructor(satsPerVbyte = 5) {
|
|
112472
|
+
if (satsPerVbyte < 0 || !Number.isFinite(satsPerVbyte)) {
|
|
112473
|
+
throw new Error(`Invalid satsPerVbyte: ${satsPerVbyte}`);
|
|
112474
|
+
}
|
|
112475
|
+
this.satsPerVbyte = satsPerVbyte;
|
|
112476
|
+
}
|
|
112477
|
+
async estimateFee(vsize) {
|
|
112478
|
+
if (vsize < 0 || !Number.isFinite(vsize)) {
|
|
112479
|
+
throw new Error(`Invalid vsize: ${vsize}`);
|
|
112480
|
+
}
|
|
112481
|
+
return BigInt(Math.ceil(vsize * this.satsPerVbyte));
|
|
112482
|
+
}
|
|
112483
|
+
};
|
|
112484
|
+
var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
|
|
112485
|
+
|
|
112486
|
+
// src/core/aggregation/runner/service-runner.ts
|
|
112458
112487
|
var DEFAULT_ADVERT_REPEAT_INTERVAL_MS = 6e4;
|
|
112459
112488
|
var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEventEmitter {
|
|
112460
112489
|
/** Direct access to the underlying state machine for advanced use. */
|
|
@@ -112465,6 +112494,7 @@ ${value2}`;
|
|
|
112465
112494
|
#onOptInReceived;
|
|
112466
112495
|
#onReadyToFinalize;
|
|
112467
112496
|
#onProvideTxData;
|
|
112497
|
+
#feeEstimator;
|
|
112468
112498
|
#cohortTtlMs;
|
|
112469
112499
|
#phaseTimeoutMs;
|
|
112470
112500
|
#advertRepeatIntervalMs;
|
|
@@ -112489,6 +112519,7 @@ ${value2}`;
|
|
|
112489
112519
|
finalize: acceptedCount >= minRequired
|
|
112490
112520
|
}));
|
|
112491
112521
|
this.#onProvideTxData = options2.onProvideTxData;
|
|
112522
|
+
this.#feeEstimator = options2.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
112492
112523
|
this.#cohortTtlMs = options2.cohortTtlMs;
|
|
112493
112524
|
this.#phaseTimeoutMs = options2.phaseTimeoutMs;
|
|
112494
112525
|
this.#advertRepeatIntervalMs = options2.advertRepeatIntervalMs ?? DEFAULT_ADVERT_REPEAT_INTERVAL_MS;
|
|
@@ -112919,7 +112950,8 @@ ${value2}`;
|
|
|
112919
112950
|
const txData = await this.#onProvideTxData({
|
|
112920
112951
|
cohortId: ctx.cohortId,
|
|
112921
112952
|
beaconAddress: cohort.beaconAddress,
|
|
112922
|
-
signalBytes: cohort.signalBytes
|
|
112953
|
+
signalBytes: cohort.signalBytes,
|
|
112954
|
+
feeEstimator: this.#feeEstimator
|
|
112923
112955
|
});
|
|
112924
112956
|
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
112925
112957
|
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
@@ -113375,6 +113407,7 @@ ${value2}`;
|
|
|
113375
113407
|
keys: options2.service.keys,
|
|
113376
113408
|
config: { minParticipants: 1, network: options2.config.network, beaconType: options2.config.beaconType, recoveryKey, recoverySequence, fundingModel: DEFAULT_FUNDING_MODEL },
|
|
113377
113409
|
onProvideTxData: options2.onProvideTxData,
|
|
113410
|
+
feeEstimator: options2.feeEstimator,
|
|
113378
113411
|
cohortTtlMs: options2.cohortTtlMs,
|
|
113379
113412
|
phaseTimeoutMs: options2.phaseTimeoutMs,
|
|
113380
113413
|
// In-process bus with the participant already listening: a single advert
|
|
@@ -113430,29 +113463,7 @@ ${value2}`;
|
|
|
113430
113463
|
}
|
|
113431
113464
|
};
|
|
113432
113465
|
|
|
113433
|
-
// src/core/beacon/fee-estimator.ts
|
|
113434
|
-
init_shim();
|
|
113435
|
-
var StaticFeeEstimator = class {
|
|
113436
|
-
satsPerVbyte;
|
|
113437
|
-
/**
|
|
113438
|
-
* @param satsPerVbyte Fee rate in satoshis per virtual byte. Default: 5 sat/vB.
|
|
113439
|
-
*/
|
|
113440
|
-
constructor(satsPerVbyte = 5) {
|
|
113441
|
-
if (satsPerVbyte < 0 || !Number.isFinite(satsPerVbyte)) {
|
|
113442
|
-
throw new Error(`Invalid satsPerVbyte: ${satsPerVbyte}`);
|
|
113443
|
-
}
|
|
113444
|
-
this.satsPerVbyte = satsPerVbyte;
|
|
113445
|
-
}
|
|
113446
|
-
async estimateFee(vsize) {
|
|
113447
|
-
if (vsize < 0 || !Number.isFinite(vsize)) {
|
|
113448
|
-
throw new Error(`Invalid vsize: ${vsize}`);
|
|
113449
|
-
}
|
|
113450
|
-
return BigInt(Math.ceil(vsize * this.satsPerVbyte));
|
|
113451
|
-
}
|
|
113452
|
-
};
|
|
113453
|
-
|
|
113454
113466
|
// src/core/beacon/beacon.ts
|
|
113455
|
-
var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
|
|
113456
113467
|
var P2TR_BEACON_TX_VSIZE = 160;
|
|
113457
113468
|
var P2WPKH_BEACON_TX_VSIZE = 155;
|
|
113458
113469
|
var P2PKH_BEACON_TX_VSIZE = 240;
|
|
@@ -113461,6 +113472,20 @@ ${value2}`;
|
|
|
113461
113472
|
p2wpkh: P2WPKH_BEACON_TX_VSIZE,
|
|
113462
113473
|
p2tr: P2TR_BEACON_TX_VSIZE
|
|
113463
113474
|
};
|
|
113475
|
+
var CHANGE_OUTPUT_VBYTES = {
|
|
113476
|
+
p2pkh: 34,
|
|
113477
|
+
p2wpkh: 31,
|
|
113478
|
+
p2tr: 43
|
|
113479
|
+
};
|
|
113480
|
+
var DUST_LIMIT_SATS2 = {
|
|
113481
|
+
p2pkh: 546,
|
|
113482
|
+
p2wpkh: 294,
|
|
113483
|
+
p2tr: 330
|
|
113484
|
+
};
|
|
113485
|
+
function beaconTxVsize(beaconKind, changeKind) {
|
|
113486
|
+
const base5 = SINGLETON_BEACON_TX_VSIZE[beaconKind] - CHANGE_OUTPUT_VBYTES[beaconKind];
|
|
113487
|
+
return base5 + CHANGE_OUTPUT_VBYTES[changeKind];
|
|
113488
|
+
}
|
|
113464
113489
|
function detectSingletonScriptKind(bitcoinAddress, network) {
|
|
113465
113490
|
const decoded = Address(network).decode(bitcoinAddress);
|
|
113466
113491
|
if (decoded.type === "pkh") return "p2pkh";
|
|
@@ -113477,6 +113502,26 @@ ${value2}`;
|
|
|
113477
113502
|
if (kind === "p2wpkh") return p2wpkh(pubkey, network).address;
|
|
113478
113503
|
return p2tr(pubkey.slice(1, 33), void 0, network).address;
|
|
113479
113504
|
}
|
|
113505
|
+
function resolveChangeAddress(beaconAddress, network, changeAddress) {
|
|
113506
|
+
if (!changeAddress || changeAddress === beaconAddress) return beaconAddress;
|
|
113507
|
+
try {
|
|
113508
|
+
Address(network).decode(changeAddress);
|
|
113509
|
+
} catch {
|
|
113510
|
+
throw new BeaconError(
|
|
113511
|
+
`Invalid change address "${changeAddress}" for network "${network}".`,
|
|
113512
|
+
"INVALID_CHANGE_ADDRESS",
|
|
113513
|
+
{ changeAddress, network }
|
|
113514
|
+
);
|
|
113515
|
+
}
|
|
113516
|
+
return changeAddress;
|
|
113517
|
+
}
|
|
113518
|
+
function changeOutputKind(changeAddress, network) {
|
|
113519
|
+
try {
|
|
113520
|
+
return detectSingletonScriptKind(changeAddress, network);
|
|
113521
|
+
} catch {
|
|
113522
|
+
return "p2tr";
|
|
113523
|
+
}
|
|
113524
|
+
}
|
|
113480
113525
|
function opReturnScript(signalBytes) {
|
|
113481
113526
|
return Script.encode(["RETURN", signalBytes]);
|
|
113482
113527
|
}
|
|
@@ -113503,8 +113548,10 @@ ${value2}`;
|
|
|
113503
113548
|
async function buildAggregationBeaconTx(opts) {
|
|
113504
113549
|
const feeEstimator = opts.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
113505
113550
|
const { utxo, prevTxBytes } = await fetchSpendableUtxo(opts.beaconAddress, opts.bitcoin);
|
|
113551
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, opts.network, opts.changeAddress);
|
|
113506
113552
|
const witnessScript = OutScript.encode(Address(opts.network).decode(opts.beaconAddress));
|
|
113507
|
-
const
|
|
113553
|
+
const changeKind = changeOutputKind(changeAddress, opts.network);
|
|
113554
|
+
const feeSats = await feeEstimator.estimateFee(beaconTxVsize("p2tr", changeKind));
|
|
113508
113555
|
if (BigInt(utxo.value) <= feeSats) {
|
|
113509
113556
|
throw new BeaconError(
|
|
113510
113557
|
`UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`,
|
|
@@ -113520,13 +113567,17 @@ ${value2}`;
|
|
|
113520
113567
|
witnessUtxo: { amount: BigInt(utxo.value), script: witnessScript },
|
|
113521
113568
|
tapInternalKey: opts.internalPubkey
|
|
113522
113569
|
});
|
|
113523
|
-
|
|
113570
|
+
const changeValue = BigInt(utxo.value) - feeSats;
|
|
113571
|
+
if (changeValue >= BigInt(DUST_LIMIT_SATS2[changeKind])) {
|
|
113572
|
+
tx.addOutputAddress(changeAddress, changeValue, opts.network);
|
|
113573
|
+
}
|
|
113524
113574
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
113525
113575
|
return {
|
|
113526
113576
|
tx,
|
|
113527
113577
|
prevOutScripts: [witnessScript],
|
|
113528
113578
|
prevOutValues: [BigInt(utxo.value)],
|
|
113529
113579
|
beaconAddress: opts.beaconAddress,
|
|
113580
|
+
changeAddress,
|
|
113530
113581
|
utxo,
|
|
113531
113582
|
feeSats,
|
|
113532
113583
|
scriptKind: "p2tr"
|
|
@@ -113604,7 +113655,8 @@ ${value2}`;
|
|
|
113604
113655
|
prevTxBytes,
|
|
113605
113656
|
signer,
|
|
113606
113657
|
bitcoin,
|
|
113607
|
-
feeEstimator
|
|
113658
|
+
feeEstimator,
|
|
113659
|
+
changeAddress: options2?.changeAddress
|
|
113608
113660
|
});
|
|
113609
113661
|
const signedHex = await this.signSinglePartyTx(plan, signer);
|
|
113610
113662
|
return this.broadcastRawTx(bitcoin, signedHex);
|
|
@@ -113616,13 +113668,15 @@ ${value2}`;
|
|
|
113616
113668
|
* the input accordingly. Validates that the signer's pubkey produces the beacon
|
|
113617
113669
|
* address under that script kind: without this check, a misconfigured caller
|
|
113618
113670
|
* would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
|
|
113619
|
-
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant
|
|
113620
|
-
* round-trip
|
|
113671
|
+
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant (via {@link beaconTxVsize}),
|
|
113672
|
+
* avoiding any probe-sign round-trip; a change address of a different kind re-sizes
|
|
113673
|
+
* the fee by the change output's size delta so it stays a valid upper bound.
|
|
113621
113674
|
*/
|
|
113622
113675
|
async buildSinglePartyTx(opts) {
|
|
113623
113676
|
const network = opts.bitcoin.data;
|
|
113624
113677
|
const pubkey = opts.signer.publicKey;
|
|
113625
113678
|
const kind = detectSingletonScriptKind(opts.beaconAddress, network);
|
|
113679
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, network, opts.changeAddress);
|
|
113626
113680
|
const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
|
|
113627
113681
|
if (derivedAddress !== opts.beaconAddress) {
|
|
113628
113682
|
throw new BeaconError(
|
|
@@ -113631,7 +113685,8 @@ ${value2}`;
|
|
|
113631
113685
|
{ kind, address: opts.beaconAddress, derivedAddress }
|
|
113632
113686
|
);
|
|
113633
113687
|
}
|
|
113634
|
-
const
|
|
113688
|
+
const changeKind = changeOutputKind(changeAddress, network);
|
|
113689
|
+
const feeSats = await opts.feeEstimator.estimateFee(beaconTxVsize(kind, changeKind));
|
|
113635
113690
|
const amount = BigInt(opts.utxo.value);
|
|
113636
113691
|
if (amount <= feeSats) {
|
|
113637
113692
|
throw new BeaconError(
|
|
@@ -113668,13 +113723,17 @@ ${value2}`;
|
|
|
113668
113723
|
tapInternalKey: internalKey
|
|
113669
113724
|
});
|
|
113670
113725
|
}
|
|
113671
|
-
|
|
113726
|
+
const changeValue = amount - feeSats;
|
|
113727
|
+
if (changeValue >= BigInt(DUST_LIMIT_SATS2[changeKind])) {
|
|
113728
|
+
tx.addOutputAddress(changeAddress, changeValue, network);
|
|
113729
|
+
}
|
|
113672
113730
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
113673
113731
|
return {
|
|
113674
113732
|
tx,
|
|
113675
113733
|
prevOutScripts: [prevOutScript],
|
|
113676
113734
|
prevOutValues: [amount],
|
|
113677
113735
|
beaconAddress: opts.beaconAddress,
|
|
113736
|
+
changeAddress,
|
|
113678
113737
|
utxo: opts.utxo,
|
|
113679
113738
|
feeSats,
|
|
113680
113739
|
scriptKind: kind
|
package/dist/browser.mjs
CHANGED
|
@@ -112295,6 +112295,30 @@ init_shim();
|
|
|
112295
112295
|
|
|
112296
112296
|
// src/core/aggregation/runner/service-runner.ts
|
|
112297
112297
|
init_shim();
|
|
112298
|
+
|
|
112299
|
+
// src/core/beacon/fee-estimator.ts
|
|
112300
|
+
init_shim();
|
|
112301
|
+
var StaticFeeEstimator = class {
|
|
112302
|
+
satsPerVbyte;
|
|
112303
|
+
/**
|
|
112304
|
+
* @param satsPerVbyte Fee rate in satoshis per virtual byte. Default: 5 sat/vB.
|
|
112305
|
+
*/
|
|
112306
|
+
constructor(satsPerVbyte = 5) {
|
|
112307
|
+
if (satsPerVbyte < 0 || !Number.isFinite(satsPerVbyte)) {
|
|
112308
|
+
throw new Error(`Invalid satsPerVbyte: ${satsPerVbyte}`);
|
|
112309
|
+
}
|
|
112310
|
+
this.satsPerVbyte = satsPerVbyte;
|
|
112311
|
+
}
|
|
112312
|
+
async estimateFee(vsize) {
|
|
112313
|
+
if (vsize < 0 || !Number.isFinite(vsize)) {
|
|
112314
|
+
throw new Error(`Invalid vsize: ${vsize}`);
|
|
112315
|
+
}
|
|
112316
|
+
return BigInt(Math.ceil(vsize * this.satsPerVbyte));
|
|
112317
|
+
}
|
|
112318
|
+
};
|
|
112319
|
+
var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
|
|
112320
|
+
|
|
112321
|
+
// src/core/aggregation/runner/service-runner.ts
|
|
112298
112322
|
var DEFAULT_ADVERT_REPEAT_INTERVAL_MS = 6e4;
|
|
112299
112323
|
var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEventEmitter {
|
|
112300
112324
|
/** Direct access to the underlying state machine for advanced use. */
|
|
@@ -112305,6 +112329,7 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
112305
112329
|
#onOptInReceived;
|
|
112306
112330
|
#onReadyToFinalize;
|
|
112307
112331
|
#onProvideTxData;
|
|
112332
|
+
#feeEstimator;
|
|
112308
112333
|
#cohortTtlMs;
|
|
112309
112334
|
#phaseTimeoutMs;
|
|
112310
112335
|
#advertRepeatIntervalMs;
|
|
@@ -112329,6 +112354,7 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
112329
112354
|
finalize: acceptedCount >= minRequired
|
|
112330
112355
|
}));
|
|
112331
112356
|
this.#onProvideTxData = options2.onProvideTxData;
|
|
112357
|
+
this.#feeEstimator = options2.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
112332
112358
|
this.#cohortTtlMs = options2.cohortTtlMs;
|
|
112333
112359
|
this.#phaseTimeoutMs = options2.phaseTimeoutMs;
|
|
112334
112360
|
this.#advertRepeatIntervalMs = options2.advertRepeatIntervalMs ?? DEFAULT_ADVERT_REPEAT_INTERVAL_MS;
|
|
@@ -112759,7 +112785,8 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
112759
112785
|
const txData = await this.#onProvideTxData({
|
|
112760
112786
|
cohortId: ctx.cohortId,
|
|
112761
112787
|
beaconAddress: cohort.beaconAddress,
|
|
112762
|
-
signalBytes: cohort.signalBytes
|
|
112788
|
+
signalBytes: cohort.signalBytes,
|
|
112789
|
+
feeEstimator: this.#feeEstimator
|
|
112763
112790
|
});
|
|
112764
112791
|
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
112765
112792
|
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
@@ -113215,6 +113242,7 @@ var AggregationRunner = class {
|
|
|
113215
113242
|
keys: options2.service.keys,
|
|
113216
113243
|
config: { minParticipants: 1, network: options2.config.network, beaconType: options2.config.beaconType, recoveryKey, recoverySequence, fundingModel: DEFAULT_FUNDING_MODEL },
|
|
113217
113244
|
onProvideTxData: options2.onProvideTxData,
|
|
113245
|
+
feeEstimator: options2.feeEstimator,
|
|
113218
113246
|
cohortTtlMs: options2.cohortTtlMs,
|
|
113219
113247
|
phaseTimeoutMs: options2.phaseTimeoutMs,
|
|
113220
113248
|
// In-process bus with the participant already listening: a single advert
|
|
@@ -113270,29 +113298,7 @@ var SMTBeaconError = class extends MethodError {
|
|
|
113270
113298
|
}
|
|
113271
113299
|
};
|
|
113272
113300
|
|
|
113273
|
-
// src/core/beacon/fee-estimator.ts
|
|
113274
|
-
init_shim();
|
|
113275
|
-
var StaticFeeEstimator = class {
|
|
113276
|
-
satsPerVbyte;
|
|
113277
|
-
/**
|
|
113278
|
-
* @param satsPerVbyte Fee rate in satoshis per virtual byte. Default: 5 sat/vB.
|
|
113279
|
-
*/
|
|
113280
|
-
constructor(satsPerVbyte = 5) {
|
|
113281
|
-
if (satsPerVbyte < 0 || !Number.isFinite(satsPerVbyte)) {
|
|
113282
|
-
throw new Error(`Invalid satsPerVbyte: ${satsPerVbyte}`);
|
|
113283
|
-
}
|
|
113284
|
-
this.satsPerVbyte = satsPerVbyte;
|
|
113285
|
-
}
|
|
113286
|
-
async estimateFee(vsize) {
|
|
113287
|
-
if (vsize < 0 || !Number.isFinite(vsize)) {
|
|
113288
|
-
throw new Error(`Invalid vsize: ${vsize}`);
|
|
113289
|
-
}
|
|
113290
|
-
return BigInt(Math.ceil(vsize * this.satsPerVbyte));
|
|
113291
|
-
}
|
|
113292
|
-
};
|
|
113293
|
-
|
|
113294
113301
|
// src/core/beacon/beacon.ts
|
|
113295
|
-
var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
|
|
113296
113302
|
var P2TR_BEACON_TX_VSIZE = 160;
|
|
113297
113303
|
var P2WPKH_BEACON_TX_VSIZE = 155;
|
|
113298
113304
|
var P2PKH_BEACON_TX_VSIZE = 240;
|
|
@@ -113301,6 +113307,20 @@ var SINGLETON_BEACON_TX_VSIZE = {
|
|
|
113301
113307
|
p2wpkh: P2WPKH_BEACON_TX_VSIZE,
|
|
113302
113308
|
p2tr: P2TR_BEACON_TX_VSIZE
|
|
113303
113309
|
};
|
|
113310
|
+
var CHANGE_OUTPUT_VBYTES = {
|
|
113311
|
+
p2pkh: 34,
|
|
113312
|
+
p2wpkh: 31,
|
|
113313
|
+
p2tr: 43
|
|
113314
|
+
};
|
|
113315
|
+
var DUST_LIMIT_SATS2 = {
|
|
113316
|
+
p2pkh: 546,
|
|
113317
|
+
p2wpkh: 294,
|
|
113318
|
+
p2tr: 330
|
|
113319
|
+
};
|
|
113320
|
+
function beaconTxVsize(beaconKind, changeKind) {
|
|
113321
|
+
const base5 = SINGLETON_BEACON_TX_VSIZE[beaconKind] - CHANGE_OUTPUT_VBYTES[beaconKind];
|
|
113322
|
+
return base5 + CHANGE_OUTPUT_VBYTES[changeKind];
|
|
113323
|
+
}
|
|
113304
113324
|
function detectSingletonScriptKind(bitcoinAddress, network) {
|
|
113305
113325
|
const decoded = Address(network).decode(bitcoinAddress);
|
|
113306
113326
|
if (decoded.type === "pkh") return "p2pkh";
|
|
@@ -113317,6 +113337,26 @@ function deriveSingletonAddress(kind, pubkey, network) {
|
|
|
113317
113337
|
if (kind === "p2wpkh") return p2wpkh(pubkey, network).address;
|
|
113318
113338
|
return p2tr(pubkey.slice(1, 33), void 0, network).address;
|
|
113319
113339
|
}
|
|
113340
|
+
function resolveChangeAddress(beaconAddress, network, changeAddress) {
|
|
113341
|
+
if (!changeAddress || changeAddress === beaconAddress) return beaconAddress;
|
|
113342
|
+
try {
|
|
113343
|
+
Address(network).decode(changeAddress);
|
|
113344
|
+
} catch {
|
|
113345
|
+
throw new BeaconError(
|
|
113346
|
+
`Invalid change address "${changeAddress}" for network "${network}".`,
|
|
113347
|
+
"INVALID_CHANGE_ADDRESS",
|
|
113348
|
+
{ changeAddress, network }
|
|
113349
|
+
);
|
|
113350
|
+
}
|
|
113351
|
+
return changeAddress;
|
|
113352
|
+
}
|
|
113353
|
+
function changeOutputKind(changeAddress, network) {
|
|
113354
|
+
try {
|
|
113355
|
+
return detectSingletonScriptKind(changeAddress, network);
|
|
113356
|
+
} catch {
|
|
113357
|
+
return "p2tr";
|
|
113358
|
+
}
|
|
113359
|
+
}
|
|
113320
113360
|
function opReturnScript(signalBytes) {
|
|
113321
113361
|
return Script.encode(["RETURN", signalBytes]);
|
|
113322
113362
|
}
|
|
@@ -113343,8 +113383,10 @@ async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
|
|
|
113343
113383
|
async function buildAggregationBeaconTx(opts) {
|
|
113344
113384
|
const feeEstimator = opts.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
|
|
113345
113385
|
const { utxo, prevTxBytes } = await fetchSpendableUtxo(opts.beaconAddress, opts.bitcoin);
|
|
113386
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, opts.network, opts.changeAddress);
|
|
113346
113387
|
const witnessScript = OutScript.encode(Address(opts.network).decode(opts.beaconAddress));
|
|
113347
|
-
const
|
|
113388
|
+
const changeKind = changeOutputKind(changeAddress, opts.network);
|
|
113389
|
+
const feeSats = await feeEstimator.estimateFee(beaconTxVsize("p2tr", changeKind));
|
|
113348
113390
|
if (BigInt(utxo.value) <= feeSats) {
|
|
113349
113391
|
throw new BeaconError(
|
|
113350
113392
|
`UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`,
|
|
@@ -113360,13 +113402,17 @@ async function buildAggregationBeaconTx(opts) {
|
|
|
113360
113402
|
witnessUtxo: { amount: BigInt(utxo.value), script: witnessScript },
|
|
113361
113403
|
tapInternalKey: opts.internalPubkey
|
|
113362
113404
|
});
|
|
113363
|
-
|
|
113405
|
+
const changeValue = BigInt(utxo.value) - feeSats;
|
|
113406
|
+
if (changeValue >= BigInt(DUST_LIMIT_SATS2[changeKind])) {
|
|
113407
|
+
tx.addOutputAddress(changeAddress, changeValue, opts.network);
|
|
113408
|
+
}
|
|
113364
113409
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
113365
113410
|
return {
|
|
113366
113411
|
tx,
|
|
113367
113412
|
prevOutScripts: [witnessScript],
|
|
113368
113413
|
prevOutValues: [BigInt(utxo.value)],
|
|
113369
113414
|
beaconAddress: opts.beaconAddress,
|
|
113415
|
+
changeAddress,
|
|
113370
113416
|
utxo,
|
|
113371
113417
|
feeSats,
|
|
113372
113418
|
scriptKind: "p2tr"
|
|
@@ -113444,7 +113490,8 @@ var SinglePartyBeacon = class {
|
|
|
113444
113490
|
prevTxBytes,
|
|
113445
113491
|
signer,
|
|
113446
113492
|
bitcoin,
|
|
113447
|
-
feeEstimator
|
|
113493
|
+
feeEstimator,
|
|
113494
|
+
changeAddress: options2?.changeAddress
|
|
113448
113495
|
});
|
|
113449
113496
|
const signedHex = await this.signSinglePartyTx(plan, signer);
|
|
113450
113497
|
return this.broadcastRawTx(bitcoin, signedHex);
|
|
@@ -113456,13 +113503,15 @@ var SinglePartyBeacon = class {
|
|
|
113456
113503
|
* the input accordingly. Validates that the signer's pubkey produces the beacon
|
|
113457
113504
|
* address under that script kind: without this check, a misconfigured caller
|
|
113458
113505
|
* would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
|
|
113459
|
-
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant
|
|
113460
|
-
* round-trip
|
|
113506
|
+
* the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant (via {@link beaconTxVsize}),
|
|
113507
|
+
* avoiding any probe-sign round-trip; a change address of a different kind re-sizes
|
|
113508
|
+
* the fee by the change output's size delta so it stays a valid upper bound.
|
|
113461
113509
|
*/
|
|
113462
113510
|
async buildSinglePartyTx(opts) {
|
|
113463
113511
|
const network = opts.bitcoin.data;
|
|
113464
113512
|
const pubkey = opts.signer.publicKey;
|
|
113465
113513
|
const kind = detectSingletonScriptKind(opts.beaconAddress, network);
|
|
113514
|
+
const changeAddress = resolveChangeAddress(opts.beaconAddress, network, opts.changeAddress);
|
|
113466
113515
|
const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
|
|
113467
113516
|
if (derivedAddress !== opts.beaconAddress) {
|
|
113468
113517
|
throw new BeaconError(
|
|
@@ -113471,7 +113520,8 @@ var SinglePartyBeacon = class {
|
|
|
113471
113520
|
{ kind, address: opts.beaconAddress, derivedAddress }
|
|
113472
113521
|
);
|
|
113473
113522
|
}
|
|
113474
|
-
const
|
|
113523
|
+
const changeKind = changeOutputKind(changeAddress, network);
|
|
113524
|
+
const feeSats = await opts.feeEstimator.estimateFee(beaconTxVsize(kind, changeKind));
|
|
113475
113525
|
const amount = BigInt(opts.utxo.value);
|
|
113476
113526
|
if (amount <= feeSats) {
|
|
113477
113527
|
throw new BeaconError(
|
|
@@ -113508,13 +113558,17 @@ var SinglePartyBeacon = class {
|
|
|
113508
113558
|
tapInternalKey: internalKey
|
|
113509
113559
|
});
|
|
113510
113560
|
}
|
|
113511
|
-
|
|
113561
|
+
const changeValue = amount - feeSats;
|
|
113562
|
+
if (changeValue >= BigInt(DUST_LIMIT_SATS2[changeKind])) {
|
|
113563
|
+
tx.addOutputAddress(changeAddress, changeValue, network);
|
|
113564
|
+
}
|
|
113512
113565
|
tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
|
|
113513
113566
|
return {
|
|
113514
113567
|
tx,
|
|
113515
113568
|
prevOutScripts: [prevOutScript],
|
|
113516
113569
|
prevOutValues: [amount],
|
|
113517
113570
|
beaconAddress: opts.beaconAddress,
|
|
113571
|
+
changeAddress,
|
|
113518
113572
|
utxo: opts.utxo,
|
|
113519
113573
|
feeSats,
|
|
113520
113574
|
scriptKind: kind
|
|
@@ -130247,6 +130301,7 @@ export {
|
|
|
130247
130301
|
BeaconUtils,
|
|
130248
130302
|
CASBeacon,
|
|
130249
130303
|
CASBeaconError,
|
|
130304
|
+
CHANGE_OUTPUT_VBYTES,
|
|
130250
130305
|
COHORT_ADVERT,
|
|
130251
130306
|
COHORT_OPT_IN,
|
|
130252
130307
|
COHORT_OPT_IN_ACCEPT,
|
|
@@ -130255,6 +130310,7 @@ export {
|
|
|
130255
130310
|
DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
|
|
130256
130311
|
DEFAULT_BROADCAST_LOOKBACK_MS,
|
|
130257
130312
|
DEFAULT_CLOCK_SKEW_SEC,
|
|
130313
|
+
DEFAULT_FEE_ESTIMATOR,
|
|
130258
130314
|
DEFAULT_FUNDING_MODEL,
|
|
130259
130315
|
DEFAULT_MAX_UPDATE_SIZE_BYTES,
|
|
130260
130316
|
DEFAULT_NONCE_LEN_BYTES,
|
|
@@ -130262,6 +130318,7 @@ export {
|
|
|
130262
130318
|
DEFAULT_RECOVERY_SEQUENCE,
|
|
130263
130319
|
DID_REGEX,
|
|
130264
130320
|
DISTRIBUTE_AGGREGATED_DATA,
|
|
130321
|
+
DUST_LIMIT_SATS2 as DUST_LIMIT_SATS,
|
|
130265
130322
|
DidBtcr2,
|
|
130266
130323
|
DidCommTransport,
|
|
130267
130324
|
DidDocument,
|
|
@@ -130318,6 +130375,7 @@ export {
|
|
|
130318
130375
|
TypedEventEmitter,
|
|
130319
130376
|
Updater,
|
|
130320
130377
|
VALIDATION_ACK,
|
|
130378
|
+
beaconTxVsize,
|
|
130321
130379
|
buildAggregationBeaconTx,
|
|
130322
130380
|
buildFallbackLeaf,
|
|
130323
130381
|
buildFallbackSpend,
|
|
@@ -130369,6 +130427,7 @@ export {
|
|
|
130369
130427
|
parseRequestAuth,
|
|
130370
130428
|
parseSseStream,
|
|
130371
130429
|
registerBeaconStrategy,
|
|
130430
|
+
resolveChangeAddress,
|
|
130372
130431
|
resolveFallbackThreshold,
|
|
130373
130432
|
reviveFromWire,
|
|
130374
130433
|
signEnvelope,
|