@buildonspark/spark-sdk 0.2.2 → 0.2.4
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/CHANGELOG.md +15 -0
- package/dist/{chunk-TM6CHQXC.js → chunk-3SEOTO43.js} +1 -1
- package/dist/{chunk-2ENZX6LT.js → chunk-AAZWSPUK.js} +84 -8
- package/dist/{chunk-4JD4HIAN.js → chunk-G4MSZ6DE.js} +299 -1
- package/dist/{chunk-S2AL73MZ.js → chunk-TVUMSHWA.js} +1 -1
- package/dist/{chunk-2TUM3R6C.js → chunk-W4ZRBSWM.js} +2351 -797
- package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
- package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
- package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
- package/dist/debug.cjs +2948 -1023
- package/dist/debug.d.cts +19 -6
- package/dist/debug.d.ts +19 -6
- package/dist/debug.js +5 -5
- package/dist/graphql/objects/index.cjs +13 -1
- package/dist/graphql/objects/index.d.cts +2 -2
- package/dist/graphql/objects/index.d.ts +2 -2
- package/dist/graphql/objects/index.js +1 -1
- package/dist/index.cjs +2794 -858
- package/dist/index.d.cts +190 -9
- package/dist/index.d.ts +190 -9
- package/dist/index.js +32 -6
- package/dist/index.node.cjs +2931 -892
- package/dist/index.node.d.cts +10 -188
- package/dist/index.node.d.ts +10 -188
- package/dist/index.node.js +134 -6
- package/dist/native/index.cjs +2794 -858
- package/dist/native/index.d.cts +148 -40
- package/dist/native/index.d.ts +148 -40
- package/dist/native/index.js +2799 -877
- package/dist/proto/lrc20.d.cts +1 -1
- package/dist/proto/lrc20.d.ts +1 -1
- package/dist/proto/lrc20.js +1 -1
- package/dist/proto/spark.cjs +84 -8
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark.js +1 -1
- package/dist/proto/spark_token.cjs +301 -0
- package/dist/proto/spark_token.d.cts +35 -2
- package/dist/proto/spark_token.d.ts +35 -2
- package/dist/proto/spark_token.js +8 -2
- package/dist/{sdk-types-DJ2ve9YY.d.cts → sdk-types-CB9HrW5O.d.cts} +1 -1
- package/dist/{sdk-types-DCIVdKUT.d.ts → sdk-types-CkRNraXT.d.ts} +1 -1
- package/dist/{spark-BUOx3U7Q.d.cts → spark-B_7nZx6T.d.cts} +112 -10
- package/dist/{spark-BUOx3U7Q.d.ts → spark-B_7nZx6T.d.ts} +112 -10
- package/dist/{spark-wallet-B_96y9BS.d.ts → spark-wallet-C1Tr_VKI.d.ts} +38 -28
- package/dist/{spark-wallet-CHwKQYJu.d.cts → spark-wallet-DG3x2obf.d.cts} +38 -28
- package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
- package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
- package/dist/tests/test-utils.cjs +1086 -218
- package/dist/tests/test-utils.d.cts +13 -13
- package/dist/tests/test-utils.d.ts +13 -13
- package/dist/tests/test-utils.js +56 -19
- package/dist/types/index.cjs +97 -9
- package/dist/types/index.d.cts +3 -3
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +3 -3
- package/dist/{xchain-address-D5MIHCDL.d.cts → xchain-address-BHu6CpZC.d.ts} +55 -8
- package/dist/{xchain-address-DLbW1iDh.d.ts → xchain-address-HBr6isnc.d.cts} +55 -8
- package/package.json +1 -1
- package/src/graphql/client.ts +8 -0
- package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
- package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
- package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
- package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
- package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
- package/src/graphql/objects/SwapLeaf.ts +40 -32
- package/src/graphql/objects/UserLeafInput.ts +24 -0
- package/src/graphql/objects/UserRequest.ts +4 -0
- package/src/index.node.ts +1 -1
- package/src/native/index.ts +4 -5
- package/src/proto/spark.ts +172 -16
- package/src/proto/spark_token.ts +369 -0
- package/src/services/coop-exit.ts +171 -36
- package/src/services/deposit.ts +471 -74
- package/src/services/lightning.ts +18 -5
- package/src/services/signing.ts +162 -50
- package/src/services/token-transactions.ts +6 -2
- package/src/services/transfer.ts +950 -384
- package/src/services/tree-creation.ts +342 -121
- package/src/spark-wallet/spark-wallet.node.ts +71 -66
- package/src/spark-wallet/spark-wallet.ts +459 -166
- package/src/tests/integration/coop-exit.test.ts +3 -8
- package/src/tests/integration/deposit.test.ts +3 -3
- package/src/tests/integration/lightning.test.ts +521 -466
- package/src/tests/integration/swap.test.ts +559 -307
- package/src/tests/integration/transfer.test.ts +625 -623
- package/src/tests/integration/wallet.test.ts +2 -2
- package/src/tests/integration/watchtower.test.ts +211 -0
- package/src/tests/test-utils.ts +63 -14
- package/src/tests/utils/test-faucet.ts +4 -2
- package/src/utils/adaptor-signature.ts +15 -5
- package/src/utils/fetch.ts +75 -0
- package/src/utils/mempool.ts +9 -4
- package/src/utils/transaction.ts +388 -26
package/src/services/deposit.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { schnorr, secp256k1 } from "@noble/curves/secp256k1";
|
|
2
2
|
import { sha256 } from "@noble/hashes/sha2";
|
|
3
3
|
import { hexToBytes } from "@noble/hashes/utils";
|
|
4
|
-
import * as btc from "@scure/btc-signer";
|
|
5
4
|
import { p2tr, Transaction } from "@scure/btc-signer";
|
|
6
5
|
import { equalBytes } from "@scure/btc-signer/utils";
|
|
7
6
|
import { NetworkError, ValidationError } from "../errors/types.js";
|
|
@@ -13,15 +12,16 @@ import {
|
|
|
13
12
|
StartDepositTreeCreationResponse,
|
|
14
13
|
} from "../proto/spark.js";
|
|
15
14
|
import { KeyDerivation } from "../signer/types.js";
|
|
16
|
-
import {
|
|
17
|
-
getP2TRAddressFromPublicKey,
|
|
18
|
-
getSigHashFromTx,
|
|
19
|
-
getTxId,
|
|
20
|
-
} from "../utils/bitcoin.js";
|
|
15
|
+
import { getSigHashFromTx, getTxId } from "../utils/bitcoin.js";
|
|
21
16
|
import { subtractPublicKeys } from "../utils/keys.js";
|
|
22
17
|
import { getNetwork } from "../utils/network.js";
|
|
23
18
|
import { proofOfPossessionMessageHashForDepositAddress } from "../utils/proof.js";
|
|
24
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
createRefundTxs,
|
|
21
|
+
createRootTx,
|
|
22
|
+
INITIAL_DIRECT_SEQUENCE,
|
|
23
|
+
INITIAL_SEQUENCE,
|
|
24
|
+
} from "../utils/transaction.js";
|
|
25
25
|
import { WalletConfigService } from "./config.js";
|
|
26
26
|
import { ConnectionManager } from "./connection.js";
|
|
27
27
|
|
|
@@ -185,8 +185,7 @@ export class DepositService {
|
|
|
185
185
|
depositTx,
|
|
186
186
|
vout,
|
|
187
187
|
}: CreateTreeRootParams) {
|
|
188
|
-
// Create
|
|
189
|
-
const rootTx = new Transaction({ version: 3 });
|
|
188
|
+
// Create root transactions (CPFP and direct)
|
|
190
189
|
const output = depositTx.getOutput(vout);
|
|
191
190
|
if (!output) {
|
|
192
191
|
throw new ValidationError("Invalid deposit transaction output", {
|
|
@@ -205,63 +204,461 @@ export class DepositService {
|
|
|
205
204
|
});
|
|
206
205
|
}
|
|
207
206
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/*if (outputAmount > DEFAULT_FEE_SATS) {
|
|
211
|
-
outputAmount = outputAmount - BigInt(DEFAULT_FEE_SATS);
|
|
212
|
-
}*/
|
|
213
|
-
|
|
214
|
-
// Create new output with fee-adjusted amount
|
|
215
|
-
rootTx.addInput({
|
|
216
|
-
txid: getTxId(depositTx),
|
|
207
|
+
const depositOutPoint = {
|
|
208
|
+
txid: hexToBytes(getTxId(depositTx)),
|
|
217
209
|
index: vout,
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
rootTx.addOutput({
|
|
210
|
+
};
|
|
211
|
+
const depositTxOut = {
|
|
221
212
|
script,
|
|
222
|
-
amount
|
|
223
|
-
}
|
|
213
|
+
amount,
|
|
214
|
+
};
|
|
224
215
|
|
|
225
|
-
|
|
216
|
+
const [cpfpRootTx, directRootTx] = createRootTx(
|
|
217
|
+
depositOutPoint,
|
|
218
|
+
depositTxOut,
|
|
219
|
+
);
|
|
226
220
|
|
|
227
|
-
|
|
221
|
+
// Create nonce commitments for root transactions
|
|
222
|
+
const cpfpRootNonceCommitment =
|
|
223
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
224
|
+
const directRootNonceCommitment =
|
|
228
225
|
await this.config.signer.getRandomSigningCommitment();
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
const sequence = (1 << 30) | INITIAL_TIME_LOCK;
|
|
234
|
-
/* if (outputAmount > DEFAULT_FEE_SATS) {
|
|
235
|
-
outputAmount = outputAmount - BigInt(DEFAULT_FEE_SATS);
|
|
236
|
-
}*/
|
|
237
|
-
refundTx.addInput({
|
|
238
|
-
txid: getTxId(rootTx),
|
|
239
|
-
index: 0,
|
|
240
|
-
sequence,
|
|
241
|
-
});
|
|
226
|
+
|
|
227
|
+
// Get sighashes for root transactions
|
|
228
|
+
const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
|
|
229
|
+
const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
|
|
242
230
|
|
|
243
231
|
const signingPubKey =
|
|
244
232
|
await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
245
233
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
234
|
+
// Create refund transactions (CPFP and direct)
|
|
235
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
|
|
236
|
+
createRefundTxs({
|
|
237
|
+
sequence: INITIAL_SEQUENCE,
|
|
238
|
+
directSequence: INITIAL_DIRECT_SEQUENCE,
|
|
239
|
+
input: { txid: hexToBytes(getTxId(cpfpRootTx)), index: 0 },
|
|
240
|
+
directInput: { txid: hexToBytes(getTxId(directRootTx)), index: 0 },
|
|
241
|
+
amountSats: amount,
|
|
242
|
+
receivingPubkey: signingPubKey,
|
|
243
|
+
network: this.config.getNetwork(),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Create nonce commitments for refund transactions
|
|
247
|
+
const cpfpRefundNonceCommitment =
|
|
248
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
249
|
+
const directRefundNonceCommitment =
|
|
250
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
251
|
+
const directFromCpfpRefundNonceCommitment =
|
|
252
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
253
|
+
|
|
254
|
+
// Get sighashes for refund transactions
|
|
255
|
+
const cpfpRefundTxSighash = getSigHashFromTx(
|
|
256
|
+
cpfpRefundTx,
|
|
257
|
+
0,
|
|
258
|
+
cpfpRootTx.getOutput(0),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
if (!directRefundTx || !directFromCpfpRefundTx) {
|
|
262
|
+
throw new ValidationError(
|
|
263
|
+
"Expected direct refund transactions for tree creation",
|
|
264
|
+
{
|
|
265
|
+
field: "directRefundTx",
|
|
266
|
+
value: directRefundTx,
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const directRefundTxSighash = getSigHashFromTx(
|
|
272
|
+
directRefundTx,
|
|
273
|
+
0,
|
|
274
|
+
directRootTx.getOutput(0),
|
|
275
|
+
);
|
|
276
|
+
const directFromCpfpRefundTxSighash = getSigHashFromTx(
|
|
277
|
+
directFromCpfpRefundTx,
|
|
278
|
+
0,
|
|
279
|
+
cpfpRootTx.getOutput(0),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
283
|
+
this.config.getCoordinatorAddress(),
|
|
249
284
|
);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
285
|
+
|
|
286
|
+
let treeResp: StartDepositTreeCreationResponse;
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
treeResp = await sparkClient.start_deposit_tree_creation({
|
|
290
|
+
identityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
291
|
+
onChainUtxo: {
|
|
292
|
+
vout: vout,
|
|
293
|
+
rawTx: depositTx.toBytes(true),
|
|
294
|
+
network: this.config.getNetworkProto(),
|
|
295
|
+
},
|
|
296
|
+
rootTxSigningJob: {
|
|
297
|
+
rawTx: cpfpRootTx.toBytes(),
|
|
298
|
+
signingPublicKey: signingPubKey,
|
|
299
|
+
signingNonceCommitment: cpfpRootNonceCommitment.commitment,
|
|
300
|
+
},
|
|
301
|
+
refundTxSigningJob: {
|
|
302
|
+
rawTx: cpfpRefundTx.toBytes(),
|
|
303
|
+
signingPublicKey: signingPubKey,
|
|
304
|
+
signingNonceCommitment: cpfpRefundNonceCommitment.commitment,
|
|
305
|
+
},
|
|
306
|
+
directRootTxSigningJob: {
|
|
307
|
+
rawTx: directRootTx.toBytes(),
|
|
308
|
+
signingPublicKey: signingPubKey,
|
|
309
|
+
signingNonceCommitment: directRootNonceCommitment.commitment,
|
|
310
|
+
},
|
|
311
|
+
directRefundTxSigningJob: {
|
|
312
|
+
rawTx: directRefundTx.toBytes(),
|
|
313
|
+
signingPublicKey: signingPubKey,
|
|
314
|
+
signingNonceCommitment: directRefundNonceCommitment.commitment,
|
|
315
|
+
},
|
|
316
|
+
directFromCpfpRefundTxSigningJob: {
|
|
317
|
+
rawTx: directFromCpfpRefundTx.toBytes(),
|
|
318
|
+
signingPublicKey: signingPubKey,
|
|
319
|
+
signingNonceCommitment:
|
|
320
|
+
directFromCpfpRefundNonceCommitment.commitment,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
throw new NetworkError(
|
|
325
|
+
"Failed to start deposit tree creation",
|
|
326
|
+
{
|
|
327
|
+
operation: "start_deposit_tree_creation",
|
|
328
|
+
errorCount: 1,
|
|
329
|
+
errors: error instanceof Error ? error.message : String(error),
|
|
330
|
+
},
|
|
331
|
+
error as Error,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
|
|
336
|
+
throw new ValidationError("No verifying key found in tree response", {
|
|
337
|
+
field: "verifyingKey",
|
|
338
|
+
value: treeResp.rootNodeSignatureShares,
|
|
339
|
+
expected: "Non-null verifying key",
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (
|
|
344
|
+
!treeResp.rootNodeSignatureShares.nodeTxSigningResult
|
|
345
|
+
?.signingNonceCommitments
|
|
346
|
+
) {
|
|
347
|
+
throw new ValidationError(
|
|
348
|
+
"No signing nonce commitments found in tree response",
|
|
349
|
+
{
|
|
350
|
+
field: "nodeTxSigningResult.signingNonceCommitments",
|
|
351
|
+
value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
|
|
352
|
+
expected: "Non-null signing nonce commitments",
|
|
353
|
+
},
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
!treeResp.rootNodeSignatureShares.refundTxSigningResult
|
|
359
|
+
?.signingNonceCommitments
|
|
360
|
+
) {
|
|
361
|
+
throw new ValidationError(
|
|
362
|
+
"No signing nonce commitments found in tree response",
|
|
363
|
+
{
|
|
364
|
+
field: "refundTxSigningResult.signingNonceCommitments",
|
|
365
|
+
},
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (
|
|
370
|
+
!treeResp.rootNodeSignatureShares.directNodeTxSigningResult
|
|
371
|
+
?.signingNonceCommitments
|
|
372
|
+
) {
|
|
373
|
+
throw new ValidationError(
|
|
374
|
+
"No direct node signing nonce commitments found in tree response",
|
|
375
|
+
{
|
|
376
|
+
field: "directNodeTxSigningResult.signingNonceCommitments",
|
|
377
|
+
},
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (
|
|
382
|
+
!treeResp.rootNodeSignatureShares.directRefundTxSigningResult
|
|
383
|
+
?.signingNonceCommitments
|
|
384
|
+
) {
|
|
385
|
+
throw new ValidationError(
|
|
386
|
+
"No direct refund signing nonce commitments found in tree response",
|
|
387
|
+
{
|
|
388
|
+
field: "directRefundTxSigningResult.signingNonceCommitments",
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (
|
|
394
|
+
!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult
|
|
395
|
+
?.signingNonceCommitments
|
|
396
|
+
) {
|
|
397
|
+
throw new ValidationError(
|
|
398
|
+
"No direct from CPFP refund signing nonce commitments found in tree response",
|
|
399
|
+
{
|
|
400
|
+
field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments",
|
|
401
|
+
},
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
!equalBytes(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)
|
|
407
|
+
) {
|
|
408
|
+
throw new ValidationError("Verifying key mismatch", {
|
|
409
|
+
field: "verifyingKey",
|
|
410
|
+
value: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
411
|
+
expected: verifyingKey,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Sign all four transactions
|
|
416
|
+
const cpfpRootSignature = await this.config.signer.signFrost({
|
|
417
|
+
message: cpfpRootTxSighash,
|
|
418
|
+
publicKey: signingPubKey,
|
|
419
|
+
keyDerivation,
|
|
420
|
+
verifyingKey,
|
|
421
|
+
selfCommitment: cpfpRootNonceCommitment,
|
|
422
|
+
statechainCommitments:
|
|
423
|
+
treeResp.rootNodeSignatureShares.nodeTxSigningResult
|
|
424
|
+
.signingNonceCommitments,
|
|
425
|
+
adaptorPubKey: new Uint8Array(),
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const directRootSignature = await this.config.signer.signFrost({
|
|
429
|
+
message: directRootTxSighash,
|
|
430
|
+
publicKey: signingPubKey,
|
|
431
|
+
keyDerivation,
|
|
432
|
+
verifyingKey,
|
|
433
|
+
selfCommitment: directRootNonceCommitment,
|
|
434
|
+
statechainCommitments:
|
|
435
|
+
treeResp.rootNodeSignatureShares.directNodeTxSigningResult
|
|
436
|
+
.signingNonceCommitments,
|
|
437
|
+
adaptorPubKey: new Uint8Array(),
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const cpfpRefundSignature = await this.config.signer.signFrost({
|
|
441
|
+
message: cpfpRefundTxSighash,
|
|
442
|
+
publicKey: signingPubKey,
|
|
443
|
+
keyDerivation,
|
|
444
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
445
|
+
selfCommitment: cpfpRefundNonceCommitment,
|
|
446
|
+
statechainCommitments:
|
|
447
|
+
treeResp.rootNodeSignatureShares.refundTxSigningResult
|
|
448
|
+
.signingNonceCommitments,
|
|
449
|
+
adaptorPubKey: new Uint8Array(),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const directRefundSignature = await this.config.signer.signFrost({
|
|
453
|
+
message: directRefundTxSighash,
|
|
454
|
+
publicKey: signingPubKey,
|
|
455
|
+
keyDerivation,
|
|
456
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
457
|
+
selfCommitment: directRefundNonceCommitment,
|
|
458
|
+
statechainCommitments:
|
|
459
|
+
treeResp.rootNodeSignatureShares.directRefundTxSigningResult
|
|
460
|
+
.signingNonceCommitments,
|
|
461
|
+
adaptorPubKey: new Uint8Array(),
|
|
258
462
|
});
|
|
259
463
|
|
|
260
|
-
|
|
464
|
+
const directFromCpfpRefundSignature = await this.config.signer.signFrost({
|
|
465
|
+
message: directFromCpfpRefundTxSighash,
|
|
466
|
+
publicKey: signingPubKey,
|
|
467
|
+
keyDerivation,
|
|
468
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
469
|
+
selfCommitment: directFromCpfpRefundNonceCommitment,
|
|
470
|
+
statechainCommitments:
|
|
471
|
+
treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult
|
|
472
|
+
.signingNonceCommitments,
|
|
473
|
+
adaptorPubKey: new Uint8Array(),
|
|
474
|
+
});
|
|
261
475
|
|
|
262
|
-
|
|
476
|
+
// Aggregate all four signatures
|
|
477
|
+
const cpfpRootAggregate = await this.config.signer.aggregateFrost({
|
|
478
|
+
message: cpfpRootTxSighash,
|
|
479
|
+
statechainSignatures:
|
|
480
|
+
treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
|
|
481
|
+
statechainPublicKeys:
|
|
482
|
+
treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
|
|
483
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
484
|
+
statechainCommitments:
|
|
485
|
+
treeResp.rootNodeSignatureShares.nodeTxSigningResult
|
|
486
|
+
.signingNonceCommitments,
|
|
487
|
+
selfCommitment: cpfpRootNonceCommitment,
|
|
488
|
+
publicKey: signingPubKey,
|
|
489
|
+
selfSignature: cpfpRootSignature!,
|
|
490
|
+
adaptorPubKey: new Uint8Array(),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const directRootAggregate = await this.config.signer.aggregateFrost({
|
|
494
|
+
message: directRootTxSighash,
|
|
495
|
+
statechainSignatures:
|
|
496
|
+
treeResp.rootNodeSignatureShares.directNodeTxSigningResult
|
|
497
|
+
.signatureShares,
|
|
498
|
+
statechainPublicKeys:
|
|
499
|
+
treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
|
|
500
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
501
|
+
statechainCommitments:
|
|
502
|
+
treeResp.rootNodeSignatureShares.directNodeTxSigningResult
|
|
503
|
+
.signingNonceCommitments,
|
|
504
|
+
selfCommitment: directRootNonceCommitment,
|
|
505
|
+
publicKey: signingPubKey,
|
|
506
|
+
selfSignature: directRootSignature!,
|
|
507
|
+
adaptorPubKey: new Uint8Array(),
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
|
|
511
|
+
message: cpfpRefundTxSighash,
|
|
512
|
+
statechainSignatures:
|
|
513
|
+
treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
|
|
514
|
+
statechainPublicKeys:
|
|
515
|
+
treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
|
|
516
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
517
|
+
statechainCommitments:
|
|
518
|
+
treeResp.rootNodeSignatureShares.refundTxSigningResult
|
|
519
|
+
.signingNonceCommitments,
|
|
520
|
+
selfCommitment: cpfpRefundNonceCommitment,
|
|
521
|
+
publicKey: signingPubKey,
|
|
522
|
+
selfSignature: cpfpRefundSignature!,
|
|
523
|
+
adaptorPubKey: new Uint8Array(),
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const directRefundAggregate = await this.config.signer.aggregateFrost({
|
|
527
|
+
message: directRefundTxSighash,
|
|
528
|
+
statechainSignatures:
|
|
529
|
+
treeResp.rootNodeSignatureShares.directRefundTxSigningResult
|
|
530
|
+
.signatureShares,
|
|
531
|
+
statechainPublicKeys:
|
|
532
|
+
treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
|
|
533
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
534
|
+
statechainCommitments:
|
|
535
|
+
treeResp.rootNodeSignatureShares.directRefundTxSigningResult
|
|
536
|
+
.signingNonceCommitments,
|
|
537
|
+
selfCommitment: directRefundNonceCommitment,
|
|
538
|
+
publicKey: signingPubKey,
|
|
539
|
+
selfSignature: directRefundSignature!,
|
|
540
|
+
adaptorPubKey: new Uint8Array(),
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const directFromCpfpRefundAggregate =
|
|
544
|
+
await this.config.signer.aggregateFrost({
|
|
545
|
+
message: directFromCpfpRefundTxSighash,
|
|
546
|
+
statechainSignatures:
|
|
547
|
+
treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult
|
|
548
|
+
.signatureShares,
|
|
549
|
+
statechainPublicKeys:
|
|
550
|
+
treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult
|
|
551
|
+
.publicKeys,
|
|
552
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
553
|
+
statechainCommitments:
|
|
554
|
+
treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult
|
|
555
|
+
.signingNonceCommitments,
|
|
556
|
+
selfCommitment: directFromCpfpRefundNonceCommitment,
|
|
557
|
+
publicKey: signingPubKey,
|
|
558
|
+
selfSignature: directFromCpfpRefundSignature!,
|
|
559
|
+
adaptorPubKey: new Uint8Array(),
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
let finalizeResp: FinalizeNodeSignaturesResponse;
|
|
563
|
+
try {
|
|
564
|
+
finalizeResp = await sparkClient.finalize_node_signatures_v2({
|
|
565
|
+
intent: SignatureIntent.CREATION,
|
|
566
|
+
nodeSignatures: [
|
|
567
|
+
{
|
|
568
|
+
nodeId: treeResp.rootNodeSignatureShares.nodeId,
|
|
569
|
+
nodeTxSignature: cpfpRootAggregate,
|
|
570
|
+
refundTxSignature: cpfpRefundAggregate,
|
|
571
|
+
directNodeTxSignature: directRootAggregate,
|
|
572
|
+
directRefundTxSignature: directRefundAggregate,
|
|
573
|
+
directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate,
|
|
574
|
+
},
|
|
575
|
+
],
|
|
576
|
+
});
|
|
577
|
+
} catch (error) {
|
|
578
|
+
throw new NetworkError(
|
|
579
|
+
"Failed to finalize node signatures",
|
|
580
|
+
{
|
|
581
|
+
operation: "finalize_node_signatures",
|
|
582
|
+
errorCount: 1,
|
|
583
|
+
errors: error instanceof Error ? error.message : String(error),
|
|
584
|
+
},
|
|
585
|
+
error as Error,
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return finalizeResp;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @deprecated
|
|
594
|
+
* Use createTreeRoot instead.
|
|
595
|
+
* This is currently only used to test backwards compatibility.
|
|
596
|
+
*/
|
|
597
|
+
async createTreeWithoutDirectTx({
|
|
598
|
+
keyDerivation,
|
|
599
|
+
verifyingKey,
|
|
600
|
+
depositTx,
|
|
601
|
+
vout,
|
|
602
|
+
}: CreateTreeRootParams) {
|
|
603
|
+
// Create root transactions (CPFP and direct)
|
|
604
|
+
const output = depositTx.getOutput(vout);
|
|
605
|
+
if (!output) {
|
|
606
|
+
throw new ValidationError("Invalid deposit transaction output", {
|
|
607
|
+
field: "vout",
|
|
608
|
+
value: vout,
|
|
609
|
+
expected: "Valid output index",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
const script = output.script;
|
|
613
|
+
const amount = output.amount;
|
|
614
|
+
if (!script || !amount) {
|
|
615
|
+
throw new ValidationError("No script or amount found in deposit tx", {
|
|
616
|
+
field: "output",
|
|
617
|
+
value: output,
|
|
618
|
+
expected: "Output with script and amount",
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const depositOutPoint = {
|
|
623
|
+
txid: hexToBytes(getTxId(depositTx)),
|
|
624
|
+
index: vout,
|
|
625
|
+
};
|
|
626
|
+
const depositTxOut = {
|
|
627
|
+
script,
|
|
628
|
+
amount,
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
|
|
632
|
+
|
|
633
|
+
// Create nonce commitments for root transactions
|
|
634
|
+
const cpfpRootNonceCommitment =
|
|
263
635
|
await this.config.signer.getRandomSigningCommitment();
|
|
264
|
-
|
|
636
|
+
|
|
637
|
+
// Get sighashes for root transactions
|
|
638
|
+
const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
|
|
639
|
+
|
|
640
|
+
const signingPubKey =
|
|
641
|
+
await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
642
|
+
|
|
643
|
+
// Create refund transactions (CPFP and direct)
|
|
644
|
+
const { cpfpRefundTx } = createRefundTxs({
|
|
645
|
+
sequence: INITIAL_SEQUENCE,
|
|
646
|
+
input: { txid: hexToBytes(getTxId(cpfpRootTx)), index: 0 },
|
|
647
|
+
amountSats: amount,
|
|
648
|
+
receivingPubkey: signingPubKey,
|
|
649
|
+
network: this.config.getNetwork(),
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Create nonce commitments for refund transactions
|
|
653
|
+
const cpfpRefundNonceCommitment =
|
|
654
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
655
|
+
|
|
656
|
+
// Get sighashes for refund transactions
|
|
657
|
+
const cpfpRefundTxSighash = getSigHashFromTx(
|
|
658
|
+
cpfpRefundTx,
|
|
659
|
+
0,
|
|
660
|
+
cpfpRootTx.getOutput(0),
|
|
661
|
+
);
|
|
265
662
|
|
|
266
663
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
267
664
|
this.config.getCoordinatorAddress(),
|
|
@@ -278,14 +675,14 @@ export class DepositService {
|
|
|
278
675
|
network: this.config.getNetworkProto(),
|
|
279
676
|
},
|
|
280
677
|
rootTxSigningJob: {
|
|
281
|
-
rawTx:
|
|
678
|
+
rawTx: cpfpRootTx.toBytes(),
|
|
282
679
|
signingPublicKey: signingPubKey,
|
|
283
|
-
signingNonceCommitment:
|
|
680
|
+
signingNonceCommitment: cpfpRootNonceCommitment.commitment,
|
|
284
681
|
},
|
|
285
682
|
refundTxSigningJob: {
|
|
286
|
-
rawTx:
|
|
683
|
+
rawTx: cpfpRefundTx.toBytes(),
|
|
287
684
|
signingPublicKey: signingPubKey,
|
|
288
|
-
signingNonceCommitment:
|
|
685
|
+
signingNonceCommitment: cpfpRefundNonceCommitment.commitment,
|
|
289
686
|
},
|
|
290
687
|
});
|
|
291
688
|
} catch (error) {
|
|
@@ -344,32 +741,32 @@ export class DepositService {
|
|
|
344
741
|
});
|
|
345
742
|
}
|
|
346
743
|
|
|
347
|
-
const
|
|
348
|
-
message:
|
|
744
|
+
const cpfpRootSignature = await this.config.signer.signFrost({
|
|
745
|
+
message: cpfpRootTxSighash,
|
|
349
746
|
publicKey: signingPubKey,
|
|
350
747
|
keyDerivation,
|
|
351
748
|
verifyingKey,
|
|
352
|
-
selfCommitment:
|
|
749
|
+
selfCommitment: cpfpRootNonceCommitment,
|
|
353
750
|
statechainCommitments:
|
|
354
751
|
treeResp.rootNodeSignatureShares.nodeTxSigningResult
|
|
355
752
|
.signingNonceCommitments,
|
|
356
753
|
adaptorPubKey: new Uint8Array(),
|
|
357
754
|
});
|
|
358
755
|
|
|
359
|
-
const
|
|
360
|
-
message:
|
|
756
|
+
const cpfpRefundSignature = await this.config.signer.signFrost({
|
|
757
|
+
message: cpfpRefundTxSighash,
|
|
361
758
|
publicKey: signingPubKey,
|
|
362
759
|
keyDerivation,
|
|
363
|
-
verifyingKey,
|
|
364
|
-
selfCommitment:
|
|
760
|
+
verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
|
|
761
|
+
selfCommitment: cpfpRefundNonceCommitment,
|
|
365
762
|
statechainCommitments:
|
|
366
763
|
treeResp.rootNodeSignatureShares.refundTxSigningResult
|
|
367
764
|
.signingNonceCommitments,
|
|
368
765
|
adaptorPubKey: new Uint8Array(),
|
|
369
766
|
});
|
|
370
767
|
|
|
371
|
-
const
|
|
372
|
-
message:
|
|
768
|
+
const cpfpRootAggregate = await this.config.signer.aggregateFrost({
|
|
769
|
+
message: cpfpRootTxSighash,
|
|
373
770
|
statechainSignatures:
|
|
374
771
|
treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
|
|
375
772
|
statechainPublicKeys:
|
|
@@ -378,14 +775,14 @@ export class DepositService {
|
|
|
378
775
|
statechainCommitments:
|
|
379
776
|
treeResp.rootNodeSignatureShares.nodeTxSigningResult
|
|
380
777
|
.signingNonceCommitments,
|
|
381
|
-
selfCommitment:
|
|
778
|
+
selfCommitment: cpfpRootNonceCommitment,
|
|
382
779
|
publicKey: signingPubKey,
|
|
383
|
-
selfSignature:
|
|
780
|
+
selfSignature: cpfpRootSignature!,
|
|
384
781
|
adaptorPubKey: new Uint8Array(),
|
|
385
782
|
});
|
|
386
783
|
|
|
387
|
-
const
|
|
388
|
-
message:
|
|
784
|
+
const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
|
|
785
|
+
message: cpfpRefundTxSighash,
|
|
389
786
|
statechainSignatures:
|
|
390
787
|
treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
|
|
391
788
|
statechainPublicKeys:
|
|
@@ -394,21 +791,21 @@ export class DepositService {
|
|
|
394
791
|
statechainCommitments:
|
|
395
792
|
treeResp.rootNodeSignatureShares.refundTxSigningResult
|
|
396
793
|
.signingNonceCommitments,
|
|
397
|
-
selfCommitment:
|
|
794
|
+
selfCommitment: cpfpRefundNonceCommitment,
|
|
398
795
|
publicKey: signingPubKey,
|
|
399
|
-
selfSignature:
|
|
796
|
+
selfSignature: cpfpRefundSignature!,
|
|
400
797
|
adaptorPubKey: new Uint8Array(),
|
|
401
798
|
});
|
|
402
799
|
|
|
403
800
|
let finalizeResp: FinalizeNodeSignaturesResponse;
|
|
404
801
|
try {
|
|
405
|
-
finalizeResp = await sparkClient.
|
|
802
|
+
finalizeResp = await sparkClient.finalize_node_signatures_v2({
|
|
406
803
|
intent: SignatureIntent.CREATION,
|
|
407
804
|
nodeSignatures: [
|
|
408
805
|
{
|
|
409
806
|
nodeId: treeResp.rootNodeSignatureShares.nodeId,
|
|
410
|
-
nodeTxSignature:
|
|
411
|
-
refundTxSignature:
|
|
807
|
+
nodeTxSignature: cpfpRootAggregate,
|
|
808
|
+
refundTxSignature: cpfpRefundAggregate,
|
|
412
809
|
},
|
|
413
810
|
],
|
|
414
811
|
});
|