@buildonspark/spark-sdk 0.3.8 → 0.4.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/CHANGELOG.md +18 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/uniffi/uniffi/spark_frost/spark_frost.kt +1361 -1367
- package/android/src/main/jniLibs/arm64-v8a/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/x86/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/x86_64/libuniffi_spark_frost.so +0 -0
- package/dist/bare/index.cjs +1342 -1346
- package/dist/bare/index.d.cts +114 -68
- package/dist/bare/index.d.ts +114 -68
- package/dist/bare/index.js +1266 -1279
- package/dist/{chunk-FXIESWE6.js → chunk-27ILUWDJ.js} +1 -1
- package/dist/{chunk-5ASXVNTM.js → chunk-4YFT7DAE.js} +1 -1
- package/dist/{chunk-FP2CRVQH.js → chunk-G3LHXHF3.js} +1045 -1308
- package/dist/{chunk-XI6FCNYG.js → chunk-JLF6WJ7K.js} +1 -1
- package/dist/{chunk-3LPEQGVJ.js → chunk-LOXWCMZL.js} +1 -1
- package/dist/{chunk-5HU5W56H.js → chunk-WICAF6BS.js} +2 -2
- package/dist/{chunk-VFN34EOX.js → chunk-YEBEN7XD.js} +258 -0
- package/dist/{client-pNpGP15j.d.cts → client-BIqiUNy4.d.cts} +1 -1
- package/dist/{client-By-N7oJS.d.ts → client-BaQf-5gD.d.ts} +1 -1
- package/dist/debug.cjs +1330 -1336
- package/dist/debug.d.cts +20 -16
- package/dist/debug.d.ts +20 -16
- package/dist/debug.js +5 -5
- package/dist/graphql/objects/index.d.cts +3 -3
- package/dist/graphql/objects/index.d.ts +3 -3
- package/dist/index.cjs +1363 -1365
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +32 -24
- package/dist/index.node.cjs +1363 -1365
- package/dist/index.node.d.cts +7 -7
- package/dist/index.node.d.ts +7 -7
- package/dist/index.node.js +31 -23
- package/dist/{logging-DMFVY384.d.ts → logging-BNGm6dBp.d.ts} +42 -37
- package/dist/{logging-DxLp34Xm.d.cts → logging-D3IfXfHG.d.cts} +42 -37
- package/dist/native/index.react-native.cjs +1505 -1366
- package/dist/native/index.react-native.d.cts +112 -58
- package/dist/native/index.react-native.d.ts +112 -58
- package/dist/native/index.react-native.js +1415 -1289
- package/dist/proto/spark.cjs +258 -0
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark.js +5 -1
- package/dist/proto/spark_token.d.cts +1 -1
- package/dist/proto/spark_token.d.ts +1 -1
- package/dist/proto/spark_token.js +2 -2
- package/dist/{spark-By6yHsrk.d.cts → spark-DOpheE8_.d.cts} +39 -2
- package/dist/{spark-By6yHsrk.d.ts → spark-DOpheE8_.d.ts} +39 -2
- package/dist/{spark-wallet.browser-C1dQknVj.d.ts → spark-wallet.browser-B2rGwjuM.d.ts} +2 -2
- package/dist/{spark-wallet.browser-CNMo3IvO.d.cts → spark-wallet.browser-Ck9No4Ks.d.cts} +2 -2
- package/dist/{spark-wallet.node-Og6__NMh.d.ts → spark-wallet.node-BqmKsGPs.d.ts} +2 -2
- package/dist/{spark-wallet.node-BZJhJZKq.d.cts → spark-wallet.node-C2TIkyt4.d.cts} +2 -2
- package/dist/tests/test-utils.cjs +1304 -1236
- package/dist/tests/test-utils.d.cts +18 -4
- package/dist/tests/test-utils.d.ts +18 -4
- package/dist/tests/test-utils.js +7 -7
- package/dist/{token-transactions-CLR3rnYi.d.cts → token-transactions-Db8mkjnU.d.cts} +2 -2
- package/dist/{token-transactions-C7yefB2S.d.ts → token-transactions-DoMcrxXQ.d.ts} +2 -2
- package/dist/types/index.cjs +256 -0
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +2 -2
- package/dist/{wallet-config-BoyMVa6n.d.ts → wallet-config-Bg3kWltL.d.ts} +42 -35
- package/dist/{wallet-config-xom-9UFF.d.cts → wallet-config-CuZKNo9S.d.cts} +42 -35
- package/ios/spark_frostFFI.xcframework/ios-arm64/SparkFrost +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/SparkFrost +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/ios/spark_frostFFI.xcframework/macos-arm64_x86_64/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/package.json +1 -1
- package/src/index.react-native.ts +8 -2
- package/src/proto/spark.ts +348 -4
- package/src/services/config.ts +5 -0
- package/src/services/coop-exit.ts +26 -107
- package/src/services/deposit.ts +12 -48
- package/src/services/signing.ts +62 -49
- package/src/services/transfer.ts +437 -722
- package/src/services/wallet-config.ts +10 -0
- package/src/services/xhr-transport.ts +13 -3
- package/src/signer/signer.react-native.ts +73 -1
- package/src/spark-wallet/proto-descriptors.ts +1 -1
- package/src/spark-wallet/spark-wallet.ts +162 -315
- package/src/spark-wallet/types.ts +2 -2
- package/src/spark_descriptors.pb +0 -0
- package/src/tests/integration/lightning.test.ts +1 -27
- package/src/tests/integration/static_deposit.test.ts +6 -9
- package/src/tests/integration/unilateral-exit.test.ts +117 -0
- package/src/tests/optimize.test.ts +31 -1
- package/src/tests/utils/signing.ts +33 -0
- package/src/tests/utils/test-faucet.ts +61 -0
- package/src/utils/optimize.ts +42 -0
- package/src/utils/transaction.ts +250 -249
- package/src/utils/unilateral-exit.ts +1 -40
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-NX5KPN5F.js";
|
|
5
5
|
import {
|
|
6
6
|
SparkTokenServiceDefinition
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JLF6WJ7K.js";
|
|
8
8
|
import {
|
|
9
9
|
mapTransferToWalletTransfer,
|
|
10
10
|
mapTreeNodeToWalletLeaf
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-4YFT7DAE.js";
|
|
12
12
|
import {
|
|
13
13
|
SatsPayment,
|
|
14
14
|
SendLeafKeyTweaks,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
TokensPayment,
|
|
19
19
|
TreeNode,
|
|
20
20
|
networkToJSON
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-YEBEN7XD.js";
|
|
22
22
|
import {
|
|
23
23
|
BitcoinNetwork_default,
|
|
24
24
|
ClaimStaticDepositFromJson,
|
|
@@ -335,7 +335,11 @@ var BASE_CONFIG = {
|
|
|
335
335
|
console: {
|
|
336
336
|
otel: false
|
|
337
337
|
},
|
|
338
|
-
events: {}
|
|
338
|
+
events: {},
|
|
339
|
+
optimizationOptions: {
|
|
340
|
+
auto: true,
|
|
341
|
+
multiplicity: 0
|
|
342
|
+
}
|
|
339
343
|
};
|
|
340
344
|
var LOCAL_WALLET_CONFIG = {
|
|
341
345
|
...BASE_CONFIG,
|
|
@@ -546,6 +550,9 @@ var WalletConfigService = class {
|
|
|
546
550
|
getEvents() {
|
|
547
551
|
return this.config.events;
|
|
548
552
|
}
|
|
553
|
+
getOptimizationOptions() {
|
|
554
|
+
return this.config.optimizationOptions;
|
|
555
|
+
}
|
|
549
556
|
};
|
|
550
557
|
|
|
551
558
|
// src/utils/bitcoin.ts
|
|
@@ -700,6 +707,7 @@ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs)
|
|
|
700
707
|
}
|
|
701
708
|
|
|
702
709
|
// src/utils/transaction.ts
|
|
710
|
+
import { hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
|
|
703
711
|
import { Transaction as Transaction2 } from "@scure/btc-signer";
|
|
704
712
|
var INITIAL_TIMELOCK = 2e3;
|
|
705
713
|
var TEST_UNILATERAL_TIMELOCK = 100;
|
|
@@ -708,9 +716,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
|
|
|
708
716
|
var HTLC_TIMELOCK_OFFSET = 70;
|
|
709
717
|
var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
|
|
710
718
|
var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
|
|
711
|
-
var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
|
|
712
719
|
var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
|
|
713
720
|
var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
|
|
721
|
+
var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
|
|
714
722
|
var ESTIMATED_TX_SIZE = 191;
|
|
715
723
|
var DEFAULT_SATS_PER_VBYTE = 5;
|
|
716
724
|
var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
|
|
@@ -720,63 +728,8 @@ function maybeApplyFee(amount) {
|
|
|
720
728
|
}
|
|
721
729
|
return amount;
|
|
722
730
|
}
|
|
723
|
-
function createRootTx(depositOutPoint, depositTxOut) {
|
|
724
|
-
const cpfpRootTx = new Transaction2({
|
|
725
|
-
version: 3,
|
|
726
|
-
allowUnknownOutputs: true
|
|
727
|
-
});
|
|
728
|
-
cpfpRootTx.addInput(depositOutPoint);
|
|
729
|
-
cpfpRootTx.addOutput(depositTxOut);
|
|
730
|
-
cpfpRootTx.addOutput(getEphemeralAnchorOutput());
|
|
731
|
-
const directRootTx = new Transaction2({
|
|
732
|
-
version: 3,
|
|
733
|
-
allowUnknownOutputs: true
|
|
734
|
-
});
|
|
735
|
-
directRootTx.addInput(depositOutPoint);
|
|
736
|
-
directRootTx.addOutput({
|
|
737
|
-
script: depositTxOut.script,
|
|
738
|
-
amount: maybeApplyFee(depositTxOut.amount ?? 0n)
|
|
739
|
-
});
|
|
740
|
-
return [cpfpRootTx, directRootTx];
|
|
741
|
-
}
|
|
742
|
-
function createSplitTx(parentOutPoint, childTxOuts) {
|
|
743
|
-
const cpfpSplitTx = new Transaction2({
|
|
744
|
-
version: 3,
|
|
745
|
-
allowUnknownOutputs: true
|
|
746
|
-
});
|
|
747
|
-
cpfpSplitTx.addInput(parentOutPoint);
|
|
748
|
-
for (const txOut of childTxOuts) {
|
|
749
|
-
cpfpSplitTx.addOutput(txOut);
|
|
750
|
-
}
|
|
751
|
-
cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
|
|
752
|
-
const directSplitTx = new Transaction2({
|
|
753
|
-
version: 3,
|
|
754
|
-
allowUnknownOutputs: true
|
|
755
|
-
});
|
|
756
|
-
directSplitTx.addInput(parentOutPoint);
|
|
757
|
-
let totalOutputAmount = 0n;
|
|
758
|
-
for (const txOut of childTxOuts) {
|
|
759
|
-
totalOutputAmount += txOut.amount ?? 0n;
|
|
760
|
-
}
|
|
761
|
-
if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
|
|
762
|
-
const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
|
|
763
|
-
for (const txOut of childTxOuts) {
|
|
764
|
-
const adjustedAmount = BigInt(
|
|
765
|
-
Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
|
|
766
|
-
);
|
|
767
|
-
directSplitTx.addOutput({
|
|
768
|
-
script: txOut.script,
|
|
769
|
-
amount: adjustedAmount
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
} else {
|
|
773
|
-
for (const txOut of childTxOuts) {
|
|
774
|
-
directSplitTx.addOutput(txOut);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
return [cpfpSplitTx, directSplitTx];
|
|
778
|
-
}
|
|
779
731
|
function createNodeTx({
|
|
732
|
+
sequence,
|
|
780
733
|
txOut,
|
|
781
734
|
parentOutPoint,
|
|
782
735
|
applyFee,
|
|
@@ -786,7 +739,10 @@ function createNodeTx({
|
|
|
786
739
|
version: 3,
|
|
787
740
|
allowUnknownOutputs: true
|
|
788
741
|
});
|
|
789
|
-
nodeTx.addInput(
|
|
742
|
+
nodeTx.addInput({
|
|
743
|
+
...parentOutPoint,
|
|
744
|
+
sequence
|
|
745
|
+
});
|
|
790
746
|
if (applyFee) {
|
|
791
747
|
nodeTx.addOutput({
|
|
792
748
|
script: txOut.script,
|
|
@@ -800,52 +756,92 @@ function createNodeTx({
|
|
|
800
756
|
}
|
|
801
757
|
return nodeTx;
|
|
802
758
|
}
|
|
803
|
-
function createNodeTxs(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
includeAnchor: false,
|
|
815
|
-
applyFee: true
|
|
759
|
+
function createNodeTxs({
|
|
760
|
+
parentTx,
|
|
761
|
+
sequence,
|
|
762
|
+
directSequence,
|
|
763
|
+
vout
|
|
764
|
+
}) {
|
|
765
|
+
const parentOutput = parentTx.getOutput(vout);
|
|
766
|
+
if (!parentOutput.amount || !parentOutput.script) {
|
|
767
|
+
throw new ValidationError("Parent output amount or script not found", {
|
|
768
|
+
field: "parentOutput",
|
|
769
|
+
value: parentOutput
|
|
816
770
|
});
|
|
817
771
|
}
|
|
818
|
-
|
|
772
|
+
const output = {
|
|
773
|
+
script: parentOutput.script,
|
|
774
|
+
amount: parentOutput.amount
|
|
775
|
+
};
|
|
776
|
+
const input = {
|
|
777
|
+
txid: hexToBytes2(getTxId(parentTx)),
|
|
778
|
+
index: vout
|
|
779
|
+
};
|
|
780
|
+
const nodeTx = createNodeTx({
|
|
781
|
+
sequence,
|
|
782
|
+
txOut: output,
|
|
783
|
+
parentOutPoint: input,
|
|
784
|
+
includeAnchor: true
|
|
785
|
+
});
|
|
786
|
+
const directNodeTx = createNodeTx({
|
|
787
|
+
sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
|
|
788
|
+
txOut: output,
|
|
789
|
+
parentOutPoint: input,
|
|
790
|
+
includeAnchor: false,
|
|
791
|
+
applyFee: true
|
|
792
|
+
});
|
|
793
|
+
return { nodeTx, directNodeTx };
|
|
819
794
|
}
|
|
820
|
-
function
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
795
|
+
function createRootNodeTx(parentTx, vout) {
|
|
796
|
+
return createNodeTxs({
|
|
797
|
+
parentTx,
|
|
798
|
+
sequence: INITIAL_ROOT_NODE_SEQUENCE,
|
|
799
|
+
vout
|
|
824
800
|
});
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
801
|
+
}
|
|
802
|
+
function createZeroTimelockNodeTx(parentTx) {
|
|
803
|
+
return createNodeTxs({
|
|
804
|
+
parentTx,
|
|
805
|
+
sequence: INITIAL_ROOT_NODE_SEQUENCE,
|
|
806
|
+
directSequence: DIRECT_TIMELOCK_OFFSET,
|
|
807
|
+
vout: 0
|
|
828
808
|
});
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
809
|
+
}
|
|
810
|
+
function createInitialTimelockNodeTx(parentTx) {
|
|
811
|
+
return createNodeTxs({
|
|
812
|
+
parentTx,
|
|
813
|
+
sequence: INITIAL_SEQUENCE,
|
|
814
|
+
vout: 0
|
|
834
815
|
});
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
816
|
+
}
|
|
817
|
+
function createDecrementedTimelockNodeTx(parentTx, currentTx) {
|
|
818
|
+
const currentSequence = currentTx.getInput(0).sequence;
|
|
819
|
+
if (!currentSequence) {
|
|
820
|
+
throw new ValidationError("Current sequence not found", {
|
|
821
|
+
field: "currentSequence",
|
|
822
|
+
value: currentSequence
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
return createNodeTxs({
|
|
826
|
+
parentTx,
|
|
827
|
+
sequence: getNextTransactionSequence(currentSequence).nextSequence,
|
|
828
|
+
vout: 0
|
|
838
829
|
});
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
830
|
+
}
|
|
831
|
+
function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
|
|
832
|
+
const sequence = nodeTx.getInput(0).sequence;
|
|
833
|
+
if (!sequence) {
|
|
834
|
+
throw new ValidationError("Sequence not found", {
|
|
835
|
+
field: "sequence",
|
|
836
|
+
value: sequence
|
|
837
|
+
});
|
|
843
838
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
839
|
+
const isBit30Defined = (sequence || 0) & 1 << 30;
|
|
840
|
+
return createNodeTxs({
|
|
841
|
+
parentTx,
|
|
842
|
+
sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
|
|
843
|
+
vout: 0
|
|
847
844
|
});
|
|
848
|
-
return [cpfpLeafTx, directLeafTx];
|
|
849
845
|
}
|
|
850
846
|
function createRefundTx({
|
|
851
847
|
sequence,
|
|
@@ -901,110 +897,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
|
|
|
901
897
|
};
|
|
902
898
|
}
|
|
903
899
|
function createRefundTxs({
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
input,
|
|
907
|
-
directInput,
|
|
908
|
-
amountSats,
|
|
900
|
+
nodeTx,
|
|
901
|
+
directNodeTx,
|
|
909
902
|
receivingPubkey,
|
|
910
|
-
network
|
|
903
|
+
network,
|
|
904
|
+
sequence
|
|
911
905
|
}) {
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
906
|
+
const refundInput = {
|
|
907
|
+
txid: hexToBytes2(getTxId(nodeTx)),
|
|
908
|
+
index: 0
|
|
909
|
+
};
|
|
910
|
+
const nodeAmountSats = nodeTx.getOutput(0).amount;
|
|
911
|
+
if (nodeAmountSats === void 0) {
|
|
912
|
+
throw new ValidationError("Node amount not found", {
|
|
913
|
+
field: "nodeAmountSats",
|
|
914
|
+
value: nodeAmountSats
|
|
915
|
+
});
|
|
916
|
+
}
|
|
921
917
|
let directRefundTx;
|
|
922
|
-
|
|
923
|
-
|
|
918
|
+
if (directNodeTx) {
|
|
919
|
+
const directRefundInput = {
|
|
920
|
+
txid: hexToBytes2(getTxId(directNodeTx)),
|
|
921
|
+
index: 0
|
|
922
|
+
};
|
|
923
|
+
const directAmountSats = directNodeTx.getOutput(0).amount;
|
|
924
|
+
if (directAmountSats === void 0) {
|
|
925
|
+
throw new ValidationError("Direct amount not found", {
|
|
926
|
+
field: "directAmountSats",
|
|
927
|
+
value: directAmountSats
|
|
928
|
+
});
|
|
929
|
+
}
|
|
924
930
|
directRefundTx = createRefundTx({
|
|
925
|
-
sequence:
|
|
926
|
-
input:
|
|
927
|
-
amountSats,
|
|
928
|
-
receivingPubkey,
|
|
929
|
-
network,
|
|
930
|
-
shouldCalculateFee: true,
|
|
931
|
-
includeAnchor: false
|
|
932
|
-
});
|
|
933
|
-
directFromCpfpRefundTx = createRefundTx({
|
|
934
|
-
sequence: directSequence,
|
|
935
|
-
input,
|
|
936
|
-
amountSats,
|
|
931
|
+
sequence: sequence + DIRECT_TIMELOCK_OFFSET,
|
|
932
|
+
input: directRefundInput,
|
|
933
|
+
amountSats: directAmountSats,
|
|
937
934
|
receivingPubkey,
|
|
938
935
|
network,
|
|
939
936
|
shouldCalculateFee: true,
|
|
940
937
|
includeAnchor: false
|
|
941
938
|
});
|
|
942
|
-
} else if (directInput && !directSequence) {
|
|
943
|
-
throw new ValidationError(
|
|
944
|
-
"directSequence must be provided if directInput is",
|
|
945
|
-
{
|
|
946
|
-
field: "directSequence",
|
|
947
|
-
value: directSequence
|
|
948
|
-
}
|
|
949
|
-
);
|
|
950
939
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
940
|
+
const cpfpRefundTx = createRefundTx({
|
|
941
|
+
sequence,
|
|
942
|
+
input: refundInput,
|
|
943
|
+
amountSats: nodeAmountSats,
|
|
944
|
+
receivingPubkey,
|
|
945
|
+
network,
|
|
946
|
+
shouldCalculateFee: false,
|
|
947
|
+
includeAnchor: true
|
|
957
948
|
});
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
949
|
+
const directFromCpfpRefundTx = createRefundTx({
|
|
950
|
+
sequence: sequence + DIRECT_TIMELOCK_OFFSET,
|
|
951
|
+
input: refundInput,
|
|
952
|
+
amountSats: nodeAmountSats,
|
|
953
|
+
receivingPubkey,
|
|
954
|
+
network,
|
|
955
|
+
shouldCalculateFee: true,
|
|
956
|
+
includeAnchor: false
|
|
961
957
|
});
|
|
962
|
-
cpfpRefundTx
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
958
|
+
return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
|
|
959
|
+
}
|
|
960
|
+
function createInitialTimelockRefundTxs(params) {
|
|
961
|
+
return createRefundTxs({
|
|
962
|
+
...params,
|
|
963
|
+
sequence: INITIAL_SEQUENCE
|
|
967
964
|
});
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
965
|
+
}
|
|
966
|
+
function createDecrementedTimelockRefundTxs(params) {
|
|
967
|
+
const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
|
|
968
|
+
return createRefundTxs({
|
|
969
|
+
...params,
|
|
970
|
+
sequence: nextSequence
|
|
971
971
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
972
|
+
}
|
|
973
|
+
function createCurrentTimelockRefundTxs(params) {
|
|
974
|
+
return createRefundTxs(params);
|
|
975
|
+
}
|
|
976
|
+
function createTestUnilateralRefundTxs(params) {
|
|
977
|
+
return createRefundTxs({
|
|
978
|
+
...params,
|
|
979
|
+
sequence: TEST_UNILATERAL_SEQUENCE
|
|
975
980
|
});
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
981
|
+
}
|
|
982
|
+
function createConnectorRefundTxs(params) {
|
|
983
|
+
const { connectorOutput, ...baseParams } = params;
|
|
984
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
|
|
985
|
+
cpfpRefundTx.addInput(connectorOutput);
|
|
986
|
+
if (directRefundTx) {
|
|
987
|
+
directRefundTx.addInput(connectorOutput);
|
|
980
988
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
}
|
|
985
|
-
const directFromCpfpTx = new Transaction2({
|
|
986
|
-
version: 3,
|
|
987
|
-
allowUnknownOutputs: true
|
|
988
|
-
});
|
|
989
|
-
directFromCpfpTx.addInput({
|
|
990
|
-
...cpfpNodeOutPoint,
|
|
991
|
-
sequence
|
|
992
|
-
});
|
|
993
|
-
directFromCpfpTx.addInput(connectorOutput);
|
|
994
|
-
directFromCpfpTx.addOutput({
|
|
995
|
-
script: receiverScript,
|
|
996
|
-
amount: outputAmount
|
|
997
|
-
});
|
|
998
|
-
return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
|
|
989
|
+
if (directFromCpfpRefundTx) {
|
|
990
|
+
directFromCpfpRefundTx.addInput(connectorOutput);
|
|
991
|
+
}
|
|
992
|
+
return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
|
|
999
993
|
}
|
|
1000
994
|
function getCurrentTimelock(currSequence) {
|
|
1001
995
|
return (currSequence || 0) & 65535;
|
|
1002
996
|
}
|
|
1003
997
|
function getTransactionSequence(currSequence) {
|
|
1004
998
|
const timelock = getCurrentTimelock(currSequence);
|
|
999
|
+
const isBit30Defined = (currSequence || 0) & 1 << 30;
|
|
1005
1000
|
return {
|
|
1006
|
-
nextSequence:
|
|
1007
|
-
nextDirectSequence:
|
|
1001
|
+
nextSequence: isBit30Defined | timelock,
|
|
1002
|
+
nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
|
|
1008
1003
|
};
|
|
1009
1004
|
}
|
|
1010
1005
|
function checkIfValidSequence(currSequence) {
|
|
@@ -1023,6 +1018,13 @@ function checkIfValidSequence(currSequence) {
|
|
|
1023
1018
|
});
|
|
1024
1019
|
}
|
|
1025
1020
|
}
|
|
1021
|
+
function isZeroTimelock(currSequence) {
|
|
1022
|
+
return getCurrentTimelock(currSequence) === 0;
|
|
1023
|
+
}
|
|
1024
|
+
function doesTxnNeedRenewed(currSequence) {
|
|
1025
|
+
const currentTimelock = getCurrentTimelock(currSequence);
|
|
1026
|
+
return currentTimelock <= 100;
|
|
1027
|
+
}
|
|
1026
1028
|
function doesLeafNeedRefresh(currSequence, isNodeTx) {
|
|
1027
1029
|
const currentTimelock = getCurrentTimelock(currSequence);
|
|
1028
1030
|
if (isNodeTx) {
|
|
@@ -1033,6 +1035,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
|
|
|
1033
1035
|
function getNextTransactionSequence(currSequence, isNodeTx) {
|
|
1034
1036
|
const currentTimelock = getCurrentTimelock(currSequence);
|
|
1035
1037
|
const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
|
|
1038
|
+
const isBit30Defined = (currSequence || 0) & 1 << 30;
|
|
1036
1039
|
if (isNodeTx && nextTimelock < 0) {
|
|
1037
1040
|
throw new ValidationError("timelock interval is less than 0", {
|
|
1038
1041
|
field: "nextTimelock",
|
|
@@ -1047,8 +1050,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
|
|
|
1047
1050
|
});
|
|
1048
1051
|
}
|
|
1049
1052
|
return {
|
|
1050
|
-
nextSequence:
|
|
1051
|
-
nextDirectSequence:
|
|
1053
|
+
nextSequence: isBit30Defined | nextTimelock,
|
|
1054
|
+
nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
|
|
1052
1055
|
};
|
|
1053
1056
|
}
|
|
1054
1057
|
function getEphemeralAnchorOutput() {
|
|
@@ -1070,7 +1073,7 @@ var KeyDerivationType = /* @__PURE__ */ ((KeyDerivationType2) => {
|
|
|
1070
1073
|
})(KeyDerivationType || {});
|
|
1071
1074
|
|
|
1072
1075
|
// src/utils/transfer_package.ts
|
|
1073
|
-
import { hexToBytes as
|
|
1076
|
+
import { hexToBytes as hexToBytes3 } from "@noble/curves/utils";
|
|
1074
1077
|
import { sha256 as sha2562 } from "@noble/hashes/sha2";
|
|
1075
1078
|
function getTransferPackageSigningPayload(transferID, transferPackage) {
|
|
1076
1079
|
const encryptedPayload = transferPackage.keyTweakPackage;
|
|
@@ -1079,7 +1082,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
|
|
|
1079
1082
|
).map(([key, value]) => ({ key, value }));
|
|
1080
1083
|
pairs.sort((a, b) => a.key.localeCompare(b.key));
|
|
1081
1084
|
const encoder = new TextEncoder();
|
|
1082
|
-
let message =
|
|
1085
|
+
let message = hexToBytes3(transferID.replaceAll("-", ""));
|
|
1083
1086
|
for (const pair of pairs) {
|
|
1084
1087
|
const keyPart = encoder.encode(pair.key + ":");
|
|
1085
1088
|
const separator = encoder.encode(";");
|
|
@@ -1207,7 +1210,7 @@ function proofOfPossessionMessageHashForDepositAddress(userPubkey, operatorPubke
|
|
|
1207
1210
|
// src/services/deposit.ts
|
|
1208
1211
|
import { schnorr as schnorr2, secp256k1 as secp256k13 } from "@noble/curves/secp256k1";
|
|
1209
1212
|
import { sha256 as sha2564 } from "@noble/hashes/sha2";
|
|
1210
|
-
import { hexToBytes as
|
|
1213
|
+
import { hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
|
|
1211
1214
|
import { p2tr as p2tr2 } from "@scure/btc-signer";
|
|
1212
1215
|
import { equalBytes } from "@scure/btc-signer/utils";
|
|
1213
1216
|
var DepositService = class {
|
|
@@ -1261,7 +1264,7 @@ var DepositService = class {
|
|
|
1261
1264
|
if (operator.identifier === this.config.getCoordinatorIdentifier() && !verifyCoordinatorProof) {
|
|
1262
1265
|
continue;
|
|
1263
1266
|
}
|
|
1264
|
-
const operatorPubkey2 =
|
|
1267
|
+
const operatorPubkey2 = hexToBytes4(operator.identityPublicKey);
|
|
1265
1268
|
const operatorSig = address.depositAddressProof.addressSignatures[operator.identifier];
|
|
1266
1269
|
if (!operatorSig) {
|
|
1267
1270
|
throw new ValidationError("Operator signature not found", {
|
|
@@ -1380,38 +1383,18 @@ var DepositService = class {
|
|
|
1380
1383
|
expected: "Valid output index"
|
|
1381
1384
|
});
|
|
1382
1385
|
}
|
|
1383
|
-
const
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
throw new ValidationError("No script or amount found in deposit tx", {
|
|
1387
|
-
field: "output",
|
|
1388
|
-
value: output,
|
|
1389
|
-
expected: "Output with script and amount"
|
|
1390
|
-
});
|
|
1391
|
-
}
|
|
1392
|
-
const depositOutPoint = {
|
|
1393
|
-
txid: hexToBytes3(getTxId(depositTx)),
|
|
1394
|
-
index: vout
|
|
1395
|
-
};
|
|
1396
|
-
const depositTxOut = {
|
|
1397
|
-
script,
|
|
1398
|
-
amount
|
|
1399
|
-
};
|
|
1400
|
-
const [cpfpRootTx, directRootTx] = createRootTx(
|
|
1401
|
-
depositOutPoint,
|
|
1402
|
-
depositTxOut
|
|
1386
|
+
const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
|
|
1387
|
+
depositTx,
|
|
1388
|
+
vout
|
|
1403
1389
|
);
|
|
1404
1390
|
const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
1405
1391
|
const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
1406
1392
|
const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
|
|
1407
1393
|
const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
|
|
1408
1394
|
const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
1409
|
-
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
input: { txid: hexToBytes3(getTxId(cpfpRootTx)), index: 0 },
|
|
1413
|
-
directInput: { txid: hexToBytes3(getTxId(directRootTx)), index: 0 },
|
|
1414
|
-
amountSats: amount,
|
|
1395
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
|
|
1396
|
+
nodeTx: cpfpRootTx,
|
|
1397
|
+
directNodeTx: directRootTx,
|
|
1415
1398
|
receivingPubkey: signingPubKey,
|
|
1416
1399
|
network: this.config.getNetwork()
|
|
1417
1400
|
});
|
|
@@ -1703,22 +1686,12 @@ var DepositService = class {
|
|
|
1703
1686
|
expected: "Output with script and amount"
|
|
1704
1687
|
});
|
|
1705
1688
|
}
|
|
1706
|
-
const
|
|
1707
|
-
txid: hexToBytes3(getTxId(depositTx)),
|
|
1708
|
-
index: vout
|
|
1709
|
-
};
|
|
1710
|
-
const depositTxOut = {
|
|
1711
|
-
script,
|
|
1712
|
-
amount
|
|
1713
|
-
};
|
|
1714
|
-
const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
|
|
1689
|
+
const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
|
|
1715
1690
|
const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
1716
1691
|
const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
|
|
1717
1692
|
const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
1718
|
-
const { cpfpRefundTx } =
|
|
1719
|
-
|
|
1720
|
-
input: { txid: hexToBytes3(getTxId(cpfpRootTx)), index: 0 },
|
|
1721
|
-
amountSats: amount,
|
|
1693
|
+
const { cpfpRefundTx } = createInitialTimelockRefundTxs({
|
|
1694
|
+
nodeTx: cpfpRootTx,
|
|
1722
1695
|
receivingPubkey: signingPubKey,
|
|
1723
1696
|
network: this.config.getNetwork()
|
|
1724
1697
|
});
|
|
@@ -1879,7 +1852,7 @@ var setCrypto = (cryptoImplParam) => {
|
|
|
1879
1852
|
import { BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
1880
1853
|
import { schnorr as schnorr3, secp256k1 as secp256k14 } from "@noble/curves/secp256k1";
|
|
1881
1854
|
import { bytesToNumberBE as bytesToNumberBE2 } from "@noble/curves/utils";
|
|
1882
|
-
import { bytesToHex as bytesToHex3, hexToBytes as
|
|
1855
|
+
import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils";
|
|
1883
1856
|
import { bech32m as bech32m2 } from "@scure/base";
|
|
1884
1857
|
import { UUID } from "uuidv7";
|
|
1885
1858
|
|
|
@@ -2071,7 +2044,7 @@ function encodeSparkAddress(payload) {
|
|
|
2071
2044
|
function encodeSparkAddressWithSignature(payload, signature) {
|
|
2072
2045
|
try {
|
|
2073
2046
|
isValidPublicKey(payload.identityPublicKey);
|
|
2074
|
-
const identityPublicKey =
|
|
2047
|
+
const identityPublicKey = hexToBytes5(payload.identityPublicKey);
|
|
2075
2048
|
let sparkInvoiceFields;
|
|
2076
2049
|
if (payload.sparkInvoiceFields) {
|
|
2077
2050
|
validateSparkInvoiceFields(payload.sparkInvoiceFields);
|
|
@@ -2575,7 +2548,7 @@ import {
|
|
|
2575
2548
|
bytesToNumberBE as bytesToNumberBE4,
|
|
2576
2549
|
numberToBytesBE as numberToBytesBE2
|
|
2577
2550
|
} from "@noble/curves/utils";
|
|
2578
|
-
import { hexToBytes as
|
|
2551
|
+
import { hexToBytes as hexToBytes6 } from "@noble/hashes/utils";
|
|
2579
2552
|
|
|
2580
2553
|
// src/utils/token-hashing.ts
|
|
2581
2554
|
import { sha256 as sha2566 } from "@noble/hashes/sha2";
|
|
@@ -3895,14 +3868,14 @@ var TokenTransactionService = class {
|
|
|
3895
3868
|
}
|
|
3896
3869
|
if (receiverAddress.sparkInvoiceFields) {
|
|
3897
3870
|
return {
|
|
3898
|
-
receiverPublicKey:
|
|
3871
|
+
receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
|
|
3899
3872
|
rawTokenIdentifier,
|
|
3900
3873
|
tokenAmount: transfer.tokenAmount,
|
|
3901
3874
|
sparkInvoice: transfer.receiverSparkAddress
|
|
3902
3875
|
};
|
|
3903
3876
|
}
|
|
3904
3877
|
return {
|
|
3905
|
-
receiverPublicKey:
|
|
3878
|
+
receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
|
|
3906
3879
|
rawTokenIdentifier,
|
|
3907
3880
|
tokenAmount: transfer.tokenAmount
|
|
3908
3881
|
};
|
|
@@ -3968,7 +3941,7 @@ var TokenTransactionService = class {
|
|
|
3968
3941
|
for (const [_, operator] of Object.entries(
|
|
3969
3942
|
this.config.getSigningOperators()
|
|
3970
3943
|
)) {
|
|
3971
|
-
operatorKeys.push(
|
|
3944
|
+
operatorKeys.push(hexToBytes6(operator.identityPublicKey));
|
|
3972
3945
|
}
|
|
3973
3946
|
return operatorKeys;
|
|
3974
3947
|
}
|
|
@@ -4231,8 +4204,8 @@ var TokenTransactionService = class {
|
|
|
4231
4204
|
this.config.getCoordinatorAddress()
|
|
4232
4205
|
);
|
|
4233
4206
|
let queryParams = {
|
|
4234
|
-
issuerPublicKeys: issuerPublicKeys?.map(
|
|
4235
|
-
ownerPublicKeys: ownerPublicKeys?.map(
|
|
4207
|
+
issuerPublicKeys: issuerPublicKeys?.map(hexToBytes6),
|
|
4208
|
+
ownerPublicKeys: ownerPublicKeys?.map(hexToBytes6),
|
|
4236
4209
|
tokenIdentifiers: tokenIdentifiers?.map((identifier) => {
|
|
4237
4210
|
const { tokenIdentifier } = decodeBech32mTokenIdentifier(
|
|
4238
4211
|
identifier,
|
|
@@ -4240,7 +4213,7 @@ var TokenTransactionService = class {
|
|
|
4240
4213
|
);
|
|
4241
4214
|
return tokenIdentifier;
|
|
4242
4215
|
}),
|
|
4243
|
-
tokenTransactionHashes: tokenTransactionHashes?.map(
|
|
4216
|
+
tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes6),
|
|
4244
4217
|
outputIds: outputIds || [],
|
|
4245
4218
|
limit: pageSize,
|
|
4246
4219
|
offset
|
|
@@ -4345,7 +4318,7 @@ var TokenTransactionService = class {
|
|
|
4345
4318
|
}
|
|
4346
4319
|
const payload = {
|
|
4347
4320
|
finalTokenTransactionHash,
|
|
4348
|
-
operatorIdentityPublicKey:
|
|
4321
|
+
operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
|
|
4349
4322
|
};
|
|
4350
4323
|
const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
|
|
4351
4324
|
const ownerSignature = await this.signMessageWithKey(
|
|
@@ -4367,7 +4340,7 @@ var TokenTransactionService = class {
|
|
|
4367
4340
|
}
|
|
4368
4341
|
const payload = {
|
|
4369
4342
|
finalTokenTransactionHash,
|
|
4370
|
-
operatorIdentityPublicKey:
|
|
4343
|
+
operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
|
|
4371
4344
|
};
|
|
4372
4345
|
const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
|
|
4373
4346
|
const ownerSignature = await this.signMessageWithKey(
|
|
@@ -4383,7 +4356,7 @@ var TokenTransactionService = class {
|
|
|
4383
4356
|
for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
|
|
4384
4357
|
const payload = {
|
|
4385
4358
|
finalTokenTransactionHash,
|
|
4386
|
-
operatorIdentityPublicKey:
|
|
4359
|
+
operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
|
|
4387
4360
|
};
|
|
4388
4361
|
const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
|
|
4389
4362
|
let ownerSignature;
|
|
@@ -4400,7 +4373,7 @@ var TokenTransactionService = class {
|
|
|
4400
4373
|
}
|
|
4401
4374
|
inputTtxoSignaturesPerOperator.push({
|
|
4402
4375
|
ttxoSignatures,
|
|
4403
|
-
operatorIdentityPublicKey:
|
|
4376
|
+
operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
|
|
4404
4377
|
});
|
|
4405
4378
|
}
|
|
4406
4379
|
return inputTtxoSignaturesPerOperator;
|
|
@@ -4830,7 +4803,7 @@ import {
|
|
|
4830
4803
|
bytesToHex as bytesToHex6,
|
|
4831
4804
|
bytesToNumberBE as bytesToNumberBE6,
|
|
4832
4805
|
equalBytes as equalBytes4,
|
|
4833
|
-
hexToBytes as
|
|
4806
|
+
hexToBytes as hexToBytes7
|
|
4834
4807
|
} from "@noble/curves/utils";
|
|
4835
4808
|
import { sha256 as sha2567 } from "@noble/hashes/sha2";
|
|
4836
4809
|
import { HDKey } from "@scure/bip32";
|
|
@@ -4848,7 +4821,7 @@ var isWebExtension = (
|
|
|
4848
4821
|
"chrome" in globalThis && globalThis.chrome.runtime?.id
|
|
4849
4822
|
);
|
|
4850
4823
|
var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
|
|
4851
|
-
var packageVersion = true ? "0.
|
|
4824
|
+
var packageVersion = true ? "0.4.0" : "unknown";
|
|
4852
4825
|
var baseEnvStr = "unknown";
|
|
4853
4826
|
if (isBun) {
|
|
4854
4827
|
const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
|
|
@@ -5228,7 +5201,7 @@ var DefaultSparkSigner = class {
|
|
|
5228
5201
|
}
|
|
5229
5202
|
async createSparkWalletFromSeed(seed, accountNumber) {
|
|
5230
5203
|
if (typeof seed === "string") {
|
|
5231
|
-
seed =
|
|
5204
|
+
seed = hexToBytes7(seed);
|
|
5232
5205
|
}
|
|
5233
5206
|
const {
|
|
5234
5207
|
identityKey,
|
|
@@ -5367,7 +5340,7 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
|
|
|
5367
5340
|
|
|
5368
5341
|
// src/tests/utils/test-faucet.ts
|
|
5369
5342
|
import { schnorr as schnorr6, secp256k1 as secp256k19 } from "@noble/curves/secp256k1";
|
|
5370
|
-
import { bytesToHex as bytesToHex7, hexToBytes as
|
|
5343
|
+
import { bytesToHex as bytesToHex7, hexToBytes as hexToBytes8 } from "@noble/curves/utils";
|
|
5371
5344
|
import * as btc3 from "@scure/btc-signer";
|
|
5372
5345
|
import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction4 } from "@scure/btc-signer";
|
|
5373
5346
|
import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
|
|
@@ -5394,10 +5367,10 @@ var getFetch = () => {
|
|
|
5394
5367
|
};
|
|
5395
5368
|
|
|
5396
5369
|
// src/tests/utils/test-faucet.ts
|
|
5397
|
-
var STATIC_FAUCET_KEY =
|
|
5370
|
+
var STATIC_FAUCET_KEY = hexToBytes8(
|
|
5398
5371
|
"deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
|
|
5399
5372
|
);
|
|
5400
|
-
var STATIC_MINING_KEY =
|
|
5373
|
+
var STATIC_MINING_KEY = hexToBytes8(
|
|
5401
5374
|
"1337cafe4242deadbeef4242424242421337cafe4242deadbeef424242424242"
|
|
5402
5375
|
);
|
|
5403
5376
|
var SATS_PER_BTC = 1e8;
|
|
@@ -5480,7 +5453,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
|
|
|
5480
5453
|
);
|
|
5481
5454
|
await this.generateToAddress(1, address);
|
|
5482
5455
|
const fundingTxRaw = await this.getRawTransaction(fundingTxid);
|
|
5483
|
-
const fundingTx = Transaction4.fromRaw(
|
|
5456
|
+
const fundingTx = Transaction4.fromRaw(hexToBytes8(fundingTxRaw.hex));
|
|
5484
5457
|
for (let i = 0; i < fundingTx.outputsLength; i++) {
|
|
5485
5458
|
const output = fundingTx.getOutput(i);
|
|
5486
5459
|
if (!output.script || !output.amount) continue;
|
|
@@ -5546,7 +5519,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
|
|
|
5546
5519
|
this.coins.push({
|
|
5547
5520
|
key: STATIC_FAUCET_KEY,
|
|
5548
5521
|
outpoint: {
|
|
5549
|
-
txid:
|
|
5522
|
+
txid: hexToBytes8(splitTxId),
|
|
5550
5523
|
index: i
|
|
5551
5524
|
},
|
|
5552
5525
|
txout: signedSplitTx.getOutput(i)
|
|
@@ -5609,6 +5582,14 @@ var BitcoinFaucet = class _BitcoinFaucet {
|
|
|
5609
5582
|
async mineBlocks(numBlocks) {
|
|
5610
5583
|
return await this.generateToAddress(numBlocks, this.miningAddress);
|
|
5611
5584
|
}
|
|
5585
|
+
async mineBlocksAndWaitForMiningToComplete(numBlocks) {
|
|
5586
|
+
const startBlock = await this.getBlockCount();
|
|
5587
|
+
await this.mineBlocks(numBlocks);
|
|
5588
|
+
await this.waitForBlocksMined({
|
|
5589
|
+
startBlock,
|
|
5590
|
+
expectedIncrease: numBlocks
|
|
5591
|
+
});
|
|
5592
|
+
}
|
|
5612
5593
|
async call(method, params) {
|
|
5613
5594
|
try {
|
|
5614
5595
|
const { fetch, Headers: Headers2 } = getFetch();
|
|
@@ -5661,15 +5642,50 @@ var BitcoinFaucet = class _BitcoinFaucet {
|
|
|
5661
5642
|
async getBlock(blockHash) {
|
|
5662
5643
|
return await this.call("getblock", [blockHash, 2]);
|
|
5663
5644
|
}
|
|
5645
|
+
async getBlockCount() {
|
|
5646
|
+
return await this.call("getblockcount", []);
|
|
5647
|
+
}
|
|
5648
|
+
async waitForBlocksMined({
|
|
5649
|
+
startBlock,
|
|
5650
|
+
expectedIncrease,
|
|
5651
|
+
timeoutMs = 3e4,
|
|
5652
|
+
intervalMs = 5e3
|
|
5653
|
+
}) {
|
|
5654
|
+
const deadline = Date.now() + timeoutMs;
|
|
5655
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
5656
|
+
const start = startBlock;
|
|
5657
|
+
const target = start + expectedIncrease;
|
|
5658
|
+
while (Date.now() < deadline) {
|
|
5659
|
+
const currentBlock = await this.getBlockCount();
|
|
5660
|
+
if (currentBlock >= target) return currentBlock;
|
|
5661
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
5662
|
+
}
|
|
5663
|
+
throw new Error(
|
|
5664
|
+
`Timed out waiting for ${expectedIncrease} blocks (target height ${target})`
|
|
5665
|
+
);
|
|
5666
|
+
}
|
|
5664
5667
|
async broadcastTx(txHex) {
|
|
5665
5668
|
let response = await this.call("sendrawtransaction", [txHex, 0]);
|
|
5666
5669
|
return response;
|
|
5667
5670
|
}
|
|
5671
|
+
async submitPackage(txHexs) {
|
|
5672
|
+
let response = await this.call("submitpackage", [txHexs]);
|
|
5673
|
+
return response;
|
|
5674
|
+
}
|
|
5668
5675
|
async getNewAddress() {
|
|
5669
5676
|
const key = secp256k19.utils.randomPrivateKey();
|
|
5670
5677
|
const pubKey = secp256k19.getPublicKey(key);
|
|
5671
5678
|
return getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */);
|
|
5672
5679
|
}
|
|
5680
|
+
async getNewExternalWallet() {
|
|
5681
|
+
const key = secp256k19.utils.randomPrivateKey();
|
|
5682
|
+
const pubKey = secp256k19.getPublicKey(key);
|
|
5683
|
+
return {
|
|
5684
|
+
address: getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */),
|
|
5685
|
+
key,
|
|
5686
|
+
pubKey
|
|
5687
|
+
};
|
|
5688
|
+
}
|
|
5673
5689
|
async sendToAddress(address, amount, blocksToGenerate = 1) {
|
|
5674
5690
|
const coin = await this.fund();
|
|
5675
5691
|
if (!coin) {
|
|
@@ -6635,7 +6651,7 @@ function collectResponses(responses) {
|
|
|
6635
6651
|
}
|
|
6636
6652
|
|
|
6637
6653
|
// src/utils/unilateral-exit.ts
|
|
6638
|
-
import { bytesToHex as bytesToHex8, hexToBytes as
|
|
6654
|
+
import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes9 } from "@noble/curves/utils";
|
|
6639
6655
|
import { ripemd160 } from "@noble/hashes/legacy";
|
|
6640
6656
|
import { sha256 as sha2569 } from "@noble/hashes/sha2";
|
|
6641
6657
|
import * as btc4 from "@scure/btc-signer";
|
|
@@ -6650,7 +6666,7 @@ function isEphemeralAnchorOutput(script, amount) {
|
|
|
6650
6666
|
}
|
|
6651
6667
|
async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
|
|
6652
6668
|
const result = [];
|
|
6653
|
-
const nodes = nodeHexStrings.map((hex) => TreeNode.decode(
|
|
6669
|
+
const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes9(hex)));
|
|
6654
6670
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
6655
6671
|
for (const node of nodes) {
|
|
6656
6672
|
nodeMap.set(node.id, node);
|
|
@@ -6739,7 +6755,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
|
|
|
6739
6755
|
`Node hex string at index ${i} appears to be a raw transaction hex, not a TreeNode protobuf. Use 'leafidtohex' command to convert node IDs to proper hex strings.`
|
|
6740
6756
|
);
|
|
6741
6757
|
}
|
|
6742
|
-
const nodeBytes =
|
|
6758
|
+
const nodeBytes = hexToBytes9(hex);
|
|
6743
6759
|
const node = TreeNode.decode(nodeBytes);
|
|
6744
6760
|
if (!node.id) {
|
|
6745
6761
|
throw new Error(
|
|
@@ -6853,7 +6869,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
|
|
|
6853
6869
|
usedUtxos,
|
|
6854
6870
|
correctedParentTx
|
|
6855
6871
|
} = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
|
|
6856
|
-
const feeBumpTx = btc4.Transaction.fromPSBT(
|
|
6872
|
+
const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes9(nodeFeeBumpPsbt));
|
|
6857
6873
|
var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
|
|
6858
6874
|
var feeBumpOutPubKey = null;
|
|
6859
6875
|
for (const usedUtxo of usedUtxos) {
|
|
@@ -6908,7 +6924,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
|
|
|
6908
6924
|
void 0
|
|
6909
6925
|
);
|
|
6910
6926
|
const feeBumpTx2 = btc4.Transaction.fromPSBT(
|
|
6911
|
-
|
|
6927
|
+
hexToBytes9(refundFeeBump.feeBumpPsbt)
|
|
6912
6928
|
);
|
|
6913
6929
|
var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
|
|
6914
6930
|
var feeBumpOutPubKey = null;
|
|
@@ -7023,9 +7039,9 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
|
|
|
7023
7039
|
if (!fundingUtxo) {
|
|
7024
7040
|
throw new Error(`UTXO at index ${i} is undefined`);
|
|
7025
7041
|
}
|
|
7026
|
-
const pubKeyHash = hash160(
|
|
7042
|
+
const pubKeyHash = hash160(hexToBytes9(fundingUtxo.publicKey));
|
|
7027
7043
|
const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
|
|
7028
|
-
const providedScript =
|
|
7044
|
+
const providedScript = hexToBytes9(fundingUtxo.script);
|
|
7029
7045
|
if (bytesToHex8(scriptToUse) !== bytesToHex8(providedScript)) {
|
|
7030
7046
|
throw new Error(
|
|
7031
7047
|
`\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
|
|
@@ -7139,7 +7155,6 @@ __export(utils_exports, {
|
|
|
7139
7155
|
DIRECT_HTLC_TIMELOCK_OFFSET: () => DIRECT_HTLC_TIMELOCK_OFFSET,
|
|
7140
7156
|
DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
|
|
7141
7157
|
HTLC_TIMELOCK_OFFSET: () => HTLC_TIMELOCK_OFFSET,
|
|
7142
|
-
INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
|
|
7143
7158
|
INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
|
|
7144
7159
|
LOGGER_NAMES: () => LOGGER_NAMES,
|
|
7145
7160
|
Network: () => Network2,
|
|
@@ -7162,21 +7177,24 @@ __export(utils_exports, {
|
|
|
7162
7177
|
constructFeeBumpTx: () => constructFeeBumpTx,
|
|
7163
7178
|
constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
|
|
7164
7179
|
constructUnilateralExitTxs: () => constructUnilateralExitTxs,
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7180
|
+
createConnectorRefundTxs: () => createConnectorRefundTxs,
|
|
7181
|
+
createCurrentTimelockRefundTxs: () => createCurrentTimelockRefundTxs,
|
|
7182
|
+
createDecrementedTimelockNodeTx: () => createDecrementedTimelockNodeTx,
|
|
7183
|
+
createDecrementedTimelockRefundTxs: () => createDecrementedTimelockRefundTxs,
|
|
7184
|
+
createInitialTimelockNodeTx: () => createInitialTimelockNodeTx,
|
|
7185
|
+
createInitialTimelockRefundTxs: () => createInitialTimelockRefundTxs,
|
|
7186
|
+
createRootNodeTx: () => createRootNodeTx,
|
|
7172
7187
|
createSigningCommitment: () => createSigningCommitment,
|
|
7173
7188
|
createSigningNonce: () => createSigningNonce,
|
|
7174
|
-
|
|
7189
|
+
createTestUnilateralRefundTxs: () => createTestUnilateralRefundTxs,
|
|
7190
|
+
createTestUnilateralTimelockNodeTx: () => createTestUnilateralTimelockNodeTx,
|
|
7191
|
+
createZeroTimelockNodeTx: () => createZeroTimelockNodeTx,
|
|
7175
7192
|
decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
|
|
7176
7193
|
decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
|
|
7177
7194
|
decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
|
|
7178
7195
|
decodeSparkAddress: () => decodeSparkAddress,
|
|
7179
7196
|
doesLeafNeedRefresh: () => doesLeafNeedRefresh,
|
|
7197
|
+
doesTxnNeedRenewed: () => doesTxnNeedRenewed,
|
|
7180
7198
|
encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
|
|
7181
7199
|
encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
|
|
7182
7200
|
encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
|
|
@@ -7214,12 +7232,14 @@ __export(utils_exports, {
|
|
|
7214
7232
|
getTxFromRawTxHex: () => getTxFromRawTxHex,
|
|
7215
7233
|
getTxId: () => getTxId,
|
|
7216
7234
|
getTxIdNoReverse: () => getTxIdNoReverse,
|
|
7235
|
+
hash160: () => hash160,
|
|
7217
7236
|
isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
|
|
7218
7237
|
isLegacySparkAddress: () => isLegacySparkAddress,
|
|
7219
7238
|
isSafeForNumber: () => isSafeForNumber,
|
|
7220
7239
|
isTxBroadcast: () => isTxBroadcast,
|
|
7221
7240
|
isValidPublicKey: () => isValidPublicKey,
|
|
7222
7241
|
isValidSparkAddress: () => isValidSparkAddress,
|
|
7242
|
+
isZeroTimelock: () => isZeroTimelock,
|
|
7223
7243
|
lastKeyWithTarget: () => lastKeyWithTarget,
|
|
7224
7244
|
maybeApplyFee: () => maybeApplyFee,
|
|
7225
7245
|
modInverse: () => modInverse,
|
|
@@ -7246,11 +7266,11 @@ import {
|
|
|
7246
7266
|
bytesToHex as bytesToHex11,
|
|
7247
7267
|
bytesToNumberBE as bytesToNumberBE8,
|
|
7248
7268
|
equalBytes as equalBytes6,
|
|
7249
|
-
hexToBytes as
|
|
7269
|
+
hexToBytes as hexToBytes12
|
|
7250
7270
|
} from "@noble/curves/utils";
|
|
7251
7271
|
import { validateMnemonic } from "@scure/bip39";
|
|
7252
7272
|
import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
|
|
7253
|
-
import { Address as Address4, OutScript as OutScript3, Transaction as
|
|
7273
|
+
import { Address as Address4, OutScript as OutScript3, Transaction as Transaction7 } from "@scure/btc-signer";
|
|
7254
7274
|
import { Mutex } from "async-mutex";
|
|
7255
7275
|
import { uuidv7 as uuidv74 } from "uuidv7";
|
|
7256
7276
|
|
|
@@ -8372,28 +8392,14 @@ var SparkAuthProvider = class {
|
|
|
8372
8392
|
};
|
|
8373
8393
|
|
|
8374
8394
|
// src/services/coop-exit.ts
|
|
8375
|
-
import { Transaction as Transaction7 } from "@scure/btc-signer";
|
|
8376
8395
|
import { uuidv7 as uuidv72 } from "uuidv7";
|
|
8377
8396
|
|
|
8378
8397
|
// src/services/transfer.ts
|
|
8379
8398
|
import { secp256k1 as secp256k110 } from "@noble/curves/secp256k1";
|
|
8380
|
-
import {
|
|
8381
|
-
bytesToHex as bytesToHex10,
|
|
8382
|
-
equalBytes as equalBytes5,
|
|
8383
|
-
hexToBytes as hexToBytes9,
|
|
8384
|
-
numberToBytesBE as numberToBytesBE4
|
|
8385
|
-
} from "@noble/curves/utils";
|
|
8399
|
+
import { bytesToHex as bytesToHex10, equalBytes as equalBytes5, numberToBytesBE as numberToBytesBE4 } from "@noble/curves/utils";
|
|
8386
8400
|
import { sha256 as sha25611 } from "@noble/hashes/sha2";
|
|
8387
|
-
import { Transaction as Transaction6 } from "@scure/btc-signer";
|
|
8388
8401
|
import * as ecies2 from "eciesjs";
|
|
8389
8402
|
import { uuidv7 } from "uuidv7";
|
|
8390
|
-
function getSigningJobProto(signingJob) {
|
|
8391
|
-
return {
|
|
8392
|
-
signingPublicKey: signingJob.signingPublicKey,
|
|
8393
|
-
rawTx: signingJob.rawTx,
|
|
8394
|
-
signingNonceCommitment: signingJob.signingNonceCommitment.commitment
|
|
8395
|
-
};
|
|
8396
|
-
}
|
|
8397
8403
|
var BaseTransferService = class {
|
|
8398
8404
|
config;
|
|
8399
8405
|
connectionManager;
|
|
@@ -9075,42 +9081,27 @@ var TransferService = class extends BaseTransferService {
|
|
|
9075
9081
|
throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
|
|
9076
9082
|
}
|
|
9077
9083
|
const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
|
|
9078
|
-
const cpfpNodeOutPoint = {
|
|
9079
|
-
txid: hexToBytes9(getTxId(nodeTx)),
|
|
9080
|
-
index: 0
|
|
9081
|
-
};
|
|
9082
9084
|
let directNodeTx;
|
|
9083
|
-
let directNodeOutPoint;
|
|
9084
9085
|
if (leaf.leaf.directTx.length > 0) {
|
|
9085
9086
|
directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
|
|
9086
|
-
directNodeOutPoint = {
|
|
9087
|
-
txid: hexToBytes9(getTxId(directNodeTx)),
|
|
9088
|
-
index: 0
|
|
9089
|
-
};
|
|
9090
9087
|
}
|
|
9091
9088
|
const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
|
|
9092
|
-
const
|
|
9093
|
-
if (!
|
|
9089
|
+
const currentSequence = currRefundTx.getInput(0).sequence;
|
|
9090
|
+
if (!currentSequence) {
|
|
9094
9091
|
throw new ValidationError("Invalid refund transaction", {
|
|
9095
9092
|
field: "sequence",
|
|
9096
9093
|
value: currRefundTx.getInput(0),
|
|
9097
9094
|
expected: "Non-null sequence"
|
|
9098
9095
|
});
|
|
9099
9096
|
}
|
|
9100
|
-
const
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
}
|
|
9105
|
-
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
|
|
9106
|
-
sequence: nextSequence,
|
|
9107
|
-
directSequence: nextDirectSequence,
|
|
9108
|
-
input: cpfpNodeOutPoint,
|
|
9109
|
-
directInput: directNodeOutPoint,
|
|
9110
|
-
amountSats,
|
|
9097
|
+
const refundTxsParams = {
|
|
9098
|
+
nodeTx,
|
|
9099
|
+
directNodeTx,
|
|
9100
|
+
sequence: currentSequence,
|
|
9111
9101
|
receivingPubkey: refundSigningData.receivingPubkey,
|
|
9112
9102
|
network: this.config.getNetwork()
|
|
9113
|
-
}
|
|
9103
|
+
};
|
|
9104
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
|
|
9114
9105
|
refundSigningData.refundTx = cpfpRefundTx;
|
|
9115
9106
|
refundSigningData.directRefundTx = directRefundTx;
|
|
9116
9107
|
refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
|
|
@@ -9323,71 +9314,127 @@ var TransferService = class extends BaseTransferService {
|
|
|
9323
9314
|
throw new Error(`Error querying pending transfers by sender: ${error}`);
|
|
9324
9315
|
}
|
|
9325
9316
|
}
|
|
9326
|
-
async
|
|
9317
|
+
async renewRefundTxn(node, parentNode) {
|
|
9318
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
9319
|
+
this.config.getCoordinatorAddress()
|
|
9320
|
+
);
|
|
9321
|
+
const signingJobs = await this.createRenewRefundSigningJobs(
|
|
9322
|
+
node,
|
|
9323
|
+
parentNode
|
|
9324
|
+
);
|
|
9325
|
+
const statechainCommitments = await sparkClient.get_signing_commitments({
|
|
9326
|
+
nodeIds: [node.id],
|
|
9327
|
+
count: signingJobs.length
|
|
9328
|
+
});
|
|
9329
|
+
const mappedSigningJobs = signingJobs.map((signingJob, index) => {
|
|
9330
|
+
const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
|
|
9331
|
+
if (!signingNonceCommitments) {
|
|
9332
|
+
throw new Error("Signing nonce commitments not found");
|
|
9333
|
+
}
|
|
9334
|
+
return {
|
|
9335
|
+
...signingJob,
|
|
9336
|
+
signingNonceCommitments
|
|
9337
|
+
};
|
|
9338
|
+
});
|
|
9339
|
+
const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
|
|
9340
|
+
const renewRefundTimelockSigningJob = {
|
|
9341
|
+
nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
|
|
9342
|
+
refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
|
|
9343
|
+
directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
|
|
9344
|
+
directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
|
|
9345
|
+
directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
|
|
9346
|
+
};
|
|
9347
|
+
const response = await sparkClient.renew_leaf({
|
|
9348
|
+
leafId: node.id,
|
|
9349
|
+
signingJobs: {
|
|
9350
|
+
$case: "renewRefundTimelockSigningJob",
|
|
9351
|
+
renewRefundTimelockSigningJob
|
|
9352
|
+
}
|
|
9353
|
+
});
|
|
9354
|
+
if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
|
|
9355
|
+
throw new ValidationError("Unexpected renew result", {
|
|
9356
|
+
field: "renewResult",
|
|
9357
|
+
value: response.renewResult
|
|
9358
|
+
});
|
|
9359
|
+
}
|
|
9360
|
+
return response.renewResult?.renewRefundTimelockResult.node;
|
|
9361
|
+
}
|
|
9362
|
+
async renewNodeTxn(node, parentNode) {
|
|
9363
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
9364
|
+
this.config.getCoordinatorAddress()
|
|
9365
|
+
);
|
|
9366
|
+
const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
|
|
9367
|
+
const statechainCommitments = await sparkClient.get_signing_commitments({
|
|
9368
|
+
nodeIds: [node.id],
|
|
9369
|
+
count: signingJobs.length
|
|
9370
|
+
});
|
|
9371
|
+
const mappedSigningJobs = signingJobs.map((signingJob, index) => {
|
|
9372
|
+
const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
|
|
9373
|
+
if (!signingNonceCommitments) {
|
|
9374
|
+
throw new Error("Signing nonce commitments not found");
|
|
9375
|
+
}
|
|
9376
|
+
return {
|
|
9377
|
+
...signingJob,
|
|
9378
|
+
signingNonceCommitments
|
|
9379
|
+
};
|
|
9380
|
+
});
|
|
9381
|
+
const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
|
|
9382
|
+
const response = await sparkClient.renew_leaf({
|
|
9383
|
+
leafId: node.id,
|
|
9384
|
+
signingJobs: {
|
|
9385
|
+
$case: "renewNodeTimelockSigningJob",
|
|
9386
|
+
renewNodeTimelockSigningJob: {
|
|
9387
|
+
splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
|
|
9388
|
+
splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
|
|
9389
|
+
nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
|
|
9390
|
+
directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
|
|
9391
|
+
refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
|
|
9392
|
+
directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
|
|
9393
|
+
directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
|
|
9394
|
+
}
|
|
9395
|
+
}
|
|
9396
|
+
});
|
|
9397
|
+
if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
|
|
9398
|
+
throw new ValidationError("Unexpected renew result", {
|
|
9399
|
+
field: "renewResult",
|
|
9400
|
+
value: response.renewResult
|
|
9401
|
+
});
|
|
9402
|
+
}
|
|
9403
|
+
return response.renewResult.renewNodeTimelockResult.node;
|
|
9404
|
+
}
|
|
9405
|
+
async createRenewRefundSigningJobs(node, parentNode) {
|
|
9327
9406
|
const signingJobs = [];
|
|
9328
|
-
const
|
|
9329
|
-
const parentNodeOutput =
|
|
9407
|
+
const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
|
|
9408
|
+
const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
|
|
9409
|
+
0
|
|
9410
|
+
);
|
|
9330
9411
|
if (!parentNodeOutput) {
|
|
9331
|
-
throw Error("
|
|
9412
|
+
throw new Error("Parent node output not found");
|
|
9332
9413
|
}
|
|
9333
|
-
const
|
|
9334
|
-
const nodeInput = nodeTx.getInput(0);
|
|
9335
|
-
const nodeOutput = nodeTx.getOutput(0);
|
|
9336
|
-
if (!nodeOutput) {
|
|
9337
|
-
throw Error("Could not get node output");
|
|
9338
|
-
}
|
|
9339
|
-
let directNodeTx;
|
|
9340
|
-
let directNodeInput;
|
|
9341
|
-
if (node.directTx.length > 0) {
|
|
9342
|
-
directNodeTx = getTxFromRawTxBytes(node.directTx);
|
|
9343
|
-
directNodeInput = directNodeTx.getInput(0);
|
|
9344
|
-
}
|
|
9345
|
-
const currSequence = nodeInput.sequence;
|
|
9346
|
-
if (!currSequence) {
|
|
9347
|
-
throw new ValidationError("Invalid node transaction", {
|
|
9348
|
-
field: "sequence",
|
|
9349
|
-
value: nodeInput,
|
|
9350
|
-
expected: "Non-null sequence"
|
|
9351
|
-
});
|
|
9352
|
-
}
|
|
9353
|
-
let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
|
|
9354
|
-
currSequence,
|
|
9355
|
-
true
|
|
9356
|
-
);
|
|
9357
|
-
const output = {
|
|
9414
|
+
const unsignedParentNodeOutput = {
|
|
9358
9415
|
script: parentNodeOutput.script,
|
|
9359
9416
|
amount: parentNodeOutput.amount
|
|
9360
9417
|
};
|
|
9361
|
-
const
|
|
9362
|
-
txid: nodeInput.txid,
|
|
9363
|
-
index: nodeInput.index,
|
|
9364
|
-
sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
|
|
9365
|
-
};
|
|
9366
|
-
const newDirectInput = directNodeTx && directNodeInput ? {
|
|
9367
|
-
txid: directNodeInput.txid,
|
|
9368
|
-
index: directNodeInput.index,
|
|
9369
|
-
sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
|
|
9370
|
-
} : void 0;
|
|
9371
|
-
const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
|
|
9372
|
-
output,
|
|
9373
|
-
newNodeInput,
|
|
9374
|
-
newDirectInput
|
|
9375
|
-
);
|
|
9376
|
-
const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
|
|
9377
|
-
if (!newCpfpNodeOutput) {
|
|
9378
|
-
throw Error("Could not get new cpfp node output");
|
|
9379
|
-
}
|
|
9380
|
-
const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
|
|
9381
|
-
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
|
|
9418
|
+
const keyDerivation = {
|
|
9382
9419
|
type: "leaf" /* LEAF */,
|
|
9383
9420
|
path: node.id
|
|
9384
|
-
}
|
|
9421
|
+
};
|
|
9422
|
+
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
9423
|
+
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
9424
|
+
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
9425
|
+
const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
|
|
9385
9426
|
signingJobs.push({
|
|
9386
9427
|
signingPublicKey,
|
|
9387
|
-
rawTx:
|
|
9428
|
+
rawTx: newNodeTx.toBytes(),
|
|
9388
9429
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9389
9430
|
type: "node",
|
|
9390
|
-
parentTxOut:
|
|
9431
|
+
parentTxOut: unsignedParentNodeOutput,
|
|
9432
|
+
leafId: node.id,
|
|
9433
|
+
keyDerivation: {
|
|
9434
|
+
type: "leaf" /* LEAF */,
|
|
9435
|
+
path: node.id
|
|
9436
|
+
},
|
|
9437
|
+
verifyingKey: node.verifyingPublicKey
|
|
9391
9438
|
});
|
|
9392
9439
|
if (newDirectNodeTx) {
|
|
9393
9440
|
signingJobs.push({
|
|
@@ -9395,537 +9442,299 @@ var TransferService = class extends BaseTransferService {
|
|
|
9395
9442
|
rawTx: newDirectNodeTx.toBytes(),
|
|
9396
9443
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9397
9444
|
type: "directNode",
|
|
9398
|
-
parentTxOut:
|
|
9445
|
+
parentTxOut: unsignedParentNodeOutput,
|
|
9446
|
+
leafId: node.id,
|
|
9447
|
+
keyDerivation: {
|
|
9448
|
+
type: "leaf" /* LEAF */,
|
|
9449
|
+
path: node.id
|
|
9450
|
+
},
|
|
9451
|
+
verifyingKey: node.verifyingPublicKey
|
|
9399
9452
|
});
|
|
9400
9453
|
}
|
|
9401
|
-
const
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
};
|
|
9405
|
-
let newDirectRefundOutPoint;
|
|
9406
|
-
if (newDirectNodeTx) {
|
|
9407
|
-
newDirectRefundOutPoint = {
|
|
9408
|
-
txid: hexToBytes9(getTxId(newDirectNodeTx)),
|
|
9409
|
-
index: 0
|
|
9410
|
-
};
|
|
9454
|
+
const newCpfpNodeOutput = newNodeTx.getOutput(0);
|
|
9455
|
+
if (!newCpfpNodeOutput) {
|
|
9456
|
+
throw Error("Could not get new cpfp node output");
|
|
9411
9457
|
}
|
|
9412
|
-
const
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9458
|
+
const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
|
|
9459
|
+
const amountSats = refundTx.getOutput(0).amount;
|
|
9460
|
+
if (amountSats === void 0) {
|
|
9461
|
+
throw new Error("Amount not found in extendTimelock");
|
|
9462
|
+
}
|
|
9463
|
+
const directAmountSats = newDirectNodeOutput?.amount;
|
|
9464
|
+
if (directAmountSats === void 0) {
|
|
9465
|
+
throw new Error("Amount not found in extendTimelock");
|
|
9466
|
+
}
|
|
9467
|
+
const {
|
|
9468
|
+
cpfpRefundTx: newRefundTx,
|
|
9469
|
+
directRefundTx: newDirectRefundTx,
|
|
9470
|
+
directFromCpfpRefundTx: newDirectFromCpfpRefundTx
|
|
9471
|
+
} = createInitialTimelockRefundTxs({
|
|
9472
|
+
nodeTx: newNodeTx,
|
|
9473
|
+
directNodeTx: newDirectNodeTx,
|
|
9474
|
+
receivingPubkey: signingPublicKey,
|
|
9422
9475
|
network: this.config.getNetwork()
|
|
9423
9476
|
});
|
|
9424
9477
|
signingJobs.push({
|
|
9425
9478
|
signingPublicKey,
|
|
9426
|
-
rawTx:
|
|
9479
|
+
rawTx: newRefundTx.toBytes(),
|
|
9427
9480
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9428
9481
|
type: "cpfp",
|
|
9429
|
-
parentTxOut: newCpfpNodeOutput
|
|
9482
|
+
parentTxOut: newCpfpNodeOutput,
|
|
9483
|
+
leafId: node.id,
|
|
9484
|
+
keyDerivation,
|
|
9485
|
+
verifyingKey: node.verifyingPublicKey
|
|
9430
9486
|
});
|
|
9431
|
-
if (
|
|
9487
|
+
if (newDirectRefundTx && newDirectNodeOutput) {
|
|
9432
9488
|
signingJobs.push({
|
|
9433
9489
|
signingPublicKey,
|
|
9434
|
-
rawTx:
|
|
9490
|
+
rawTx: newDirectRefundTx.toBytes(),
|
|
9435
9491
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9436
9492
|
type: "direct",
|
|
9437
|
-
parentTxOut: newDirectNodeOutput
|
|
9493
|
+
parentTxOut: newDirectNodeOutput,
|
|
9494
|
+
leafId: node.id,
|
|
9495
|
+
keyDerivation,
|
|
9496
|
+
verifyingKey: node.verifyingPublicKey
|
|
9438
9497
|
});
|
|
9439
9498
|
}
|
|
9440
|
-
if (
|
|
9499
|
+
if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
|
|
9441
9500
|
signingJobs.push({
|
|
9442
9501
|
signingPublicKey,
|
|
9443
|
-
rawTx:
|
|
9502
|
+
rawTx: newDirectFromCpfpRefundTx.toBytes(),
|
|
9444
9503
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9445
9504
|
type: "directFromCpfp",
|
|
9446
|
-
parentTxOut: newCpfpNodeOutput
|
|
9505
|
+
parentTxOut: newCpfpNodeOutput,
|
|
9506
|
+
leafId: node.id,
|
|
9507
|
+
keyDerivation,
|
|
9508
|
+
verifyingKey: node.verifyingPublicKey
|
|
9447
9509
|
});
|
|
9448
9510
|
}
|
|
9449
|
-
|
|
9450
|
-
this.config.getCoordinatorAddress()
|
|
9451
|
-
);
|
|
9452
|
-
const response = await sparkClient.refresh_timelock_v2({
|
|
9453
|
-
leafId: node.id,
|
|
9454
|
-
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
9455
|
-
signingJobs: signingJobs.map(getSigningJobProto)
|
|
9456
|
-
});
|
|
9457
|
-
if (signingJobs.length !== response.signingResults.length) {
|
|
9458
|
-
throw Error(
|
|
9459
|
-
`number of signing jobs and signing results do not match: ${signingJobs.length} !== ${response.signingResults.length}`
|
|
9460
|
-
);
|
|
9461
|
-
}
|
|
9462
|
-
let nodeSignatures = [];
|
|
9463
|
-
let leafCpfpSignature;
|
|
9464
|
-
let leafDirectSignature;
|
|
9465
|
-
let cpfpRefundSignature;
|
|
9466
|
-
let directRefundSignature;
|
|
9467
|
-
let directFromCpfpRefundSignature;
|
|
9468
|
-
for (const [i, signingResult] of response.signingResults.entries()) {
|
|
9469
|
-
const signingJob = signingJobs[i];
|
|
9470
|
-
if (!signingJob || !signingResult) {
|
|
9471
|
-
throw Error("Signing job does not exist");
|
|
9472
|
-
}
|
|
9473
|
-
const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
|
|
9474
|
-
const txOut = signingJob.parentTxOut;
|
|
9475
|
-
if (!txOut) {
|
|
9476
|
-
throw Error("Could not get tx out");
|
|
9477
|
-
}
|
|
9478
|
-
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
9479
|
-
const userSignature = await this.config.signer.signFrost({
|
|
9480
|
-
message: rawTxSighash,
|
|
9481
|
-
keyDerivation: {
|
|
9482
|
-
type: "leaf" /* LEAF */,
|
|
9483
|
-
path: node.id
|
|
9484
|
-
},
|
|
9485
|
-
publicKey: signingJob.signingPublicKey,
|
|
9486
|
-
verifyingKey: signingResult.verifyingKey,
|
|
9487
|
-
selfCommitment: signingJob.signingNonceCommitment,
|
|
9488
|
-
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
9489
|
-
adaptorPubKey: new Uint8Array()
|
|
9490
|
-
});
|
|
9491
|
-
const signature = await this.config.signer.aggregateFrost({
|
|
9492
|
-
message: rawTxSighash,
|
|
9493
|
-
statechainSignatures: signingResult.signingResult?.signatureShares,
|
|
9494
|
-
statechainPublicKeys: signingResult.signingResult?.publicKeys,
|
|
9495
|
-
verifyingKey: signingResult.verifyingKey,
|
|
9496
|
-
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
9497
|
-
selfCommitment: signingJob.signingNonceCommitment,
|
|
9498
|
-
publicKey: signingJob.signingPublicKey,
|
|
9499
|
-
selfSignature: userSignature,
|
|
9500
|
-
adaptorPubKey: new Uint8Array()
|
|
9501
|
-
});
|
|
9502
|
-
if (signingJob.type === "node") {
|
|
9503
|
-
leafCpfpSignature = signature;
|
|
9504
|
-
} else if (signingJob.type === "directNode") {
|
|
9505
|
-
leafDirectSignature = signature;
|
|
9506
|
-
} else if (signingJob.type === "cpfp") {
|
|
9507
|
-
cpfpRefundSignature = signature;
|
|
9508
|
-
} else if (signingJob.type === "direct") {
|
|
9509
|
-
directRefundSignature = signature;
|
|
9510
|
-
} else if (signingJob.type === "directFromCpfp") {
|
|
9511
|
-
directFromCpfpRefundSignature = signature;
|
|
9512
|
-
}
|
|
9513
|
-
}
|
|
9514
|
-
nodeSignatures.push({
|
|
9515
|
-
nodeId: node.id,
|
|
9516
|
-
nodeTxSignature: leafCpfpSignature || new Uint8Array(),
|
|
9517
|
-
directNodeTxSignature: leafDirectSignature || new Uint8Array(),
|
|
9518
|
-
refundTxSignature: cpfpRefundSignature || new Uint8Array(),
|
|
9519
|
-
directRefundTxSignature: directRefundSignature || new Uint8Array(),
|
|
9520
|
-
directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
|
|
9521
|
-
});
|
|
9522
|
-
const result = await sparkClient.finalize_node_signatures_v2({
|
|
9523
|
-
intent: 3 /* REFRESH */,
|
|
9524
|
-
nodeSignatures
|
|
9525
|
-
});
|
|
9526
|
-
return result;
|
|
9527
|
-
}
|
|
9528
|
-
async refreshTimelockNodes(node, parentNode) {
|
|
9529
|
-
return await this.refreshTimelockNodesInternal(node, parentNode);
|
|
9511
|
+
return signingJobs;
|
|
9530
9512
|
}
|
|
9531
|
-
async
|
|
9532
|
-
const
|
|
9533
|
-
const
|
|
9534
|
-
const
|
|
9535
|
-
|
|
9536
|
-
txid: hexToBytes9(getTxId(nodeTx)),
|
|
9537
|
-
index: 0
|
|
9538
|
-
};
|
|
9539
|
-
const {
|
|
9540
|
-
nextSequence: newNodeSequence,
|
|
9541
|
-
nextDirectSequence: newDirectNodeSequence
|
|
9542
|
-
} = getNextTransactionSequence(refundSequence);
|
|
9543
|
-
const newNodeTx = new Transaction6({
|
|
9544
|
-
version: 3,
|
|
9545
|
-
allowUnknownOutputs: true
|
|
9546
|
-
});
|
|
9547
|
-
newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
|
|
9548
|
-
const originalOutput = nodeTx.getOutput(0);
|
|
9549
|
-
if (!originalOutput) {
|
|
9550
|
-
throw Error("Could not get original node output");
|
|
9551
|
-
}
|
|
9552
|
-
newNodeTx.addOutput({
|
|
9553
|
-
script: originalOutput.script,
|
|
9554
|
-
amount: originalOutput.amount
|
|
9555
|
-
});
|
|
9556
|
-
newNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
9557
|
-
let newDirectNodeTx;
|
|
9558
|
-
if (node.directTx.length > 0) {
|
|
9559
|
-
newDirectNodeTx = new Transaction6({
|
|
9560
|
-
version: 3,
|
|
9561
|
-
allowUnknownOutputs: true
|
|
9562
|
-
});
|
|
9563
|
-
newDirectNodeTx.addInput({
|
|
9564
|
-
...newNodeOutPoint,
|
|
9565
|
-
sequence: newDirectNodeSequence
|
|
9566
|
-
});
|
|
9567
|
-
newDirectNodeTx.addOutput({
|
|
9568
|
-
script: originalOutput.script,
|
|
9569
|
-
amount: maybeApplyFee(originalOutput.amount)
|
|
9570
|
-
});
|
|
9571
|
-
}
|
|
9572
|
-
const newCpfpRefundOutPoint = {
|
|
9573
|
-
txid: hexToBytes9(getTxId(newNodeTx)),
|
|
9574
|
-
index: 0
|
|
9575
|
-
};
|
|
9576
|
-
let newDirectRefundOutPoint;
|
|
9577
|
-
if (newDirectNodeTx) {
|
|
9578
|
-
newDirectRefundOutPoint = {
|
|
9579
|
-
txid: hexToBytes9(getTxId(newDirectNodeTx)),
|
|
9580
|
-
index: 0
|
|
9581
|
-
};
|
|
9582
|
-
}
|
|
9583
|
-
const amountSats = refundTx.getOutput(0).amount;
|
|
9584
|
-
if (amountSats === void 0) {
|
|
9585
|
-
throw new Error("Amount not found in extendTimelock");
|
|
9586
|
-
}
|
|
9587
|
-
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
|
|
9588
|
-
type: "leaf" /* LEAF */,
|
|
9589
|
-
path: node.id
|
|
9590
|
-
});
|
|
9591
|
-
const {
|
|
9592
|
-
cpfpRefundTx: newCpfpRefundTx,
|
|
9593
|
-
directRefundTx: newDirectRefundTx,
|
|
9594
|
-
directFromCpfpRefundTx: newDirectFromCpfpRefundTx
|
|
9595
|
-
} = createRefundTxs({
|
|
9596
|
-
sequence: INITIAL_SEQUENCE,
|
|
9597
|
-
directSequence: INITIAL_DIRECT_SEQUENCE,
|
|
9598
|
-
input: newCpfpRefundOutPoint,
|
|
9599
|
-
directInput: newDirectRefundOutPoint,
|
|
9600
|
-
amountSats,
|
|
9601
|
-
receivingPubkey: signingPublicKey,
|
|
9602
|
-
network: this.config.getNetwork()
|
|
9603
|
-
});
|
|
9604
|
-
if (!newCpfpRefundTx) {
|
|
9605
|
-
throw new ValidationError(
|
|
9606
|
-
"Failed to create refund transactions in extendTimelock"
|
|
9607
|
-
);
|
|
9608
|
-
}
|
|
9609
|
-
const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
|
|
9610
|
-
const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
|
|
9611
|
-
const cpfpRefundSighash = getSigHashFromTx(
|
|
9612
|
-
newCpfpRefundTx,
|
|
9613
|
-
0,
|
|
9614
|
-
newNodeTx.getOutput(0)
|
|
9513
|
+
async createRenewNodeSigningJobs(node, parentNode) {
|
|
9514
|
+
const signingJobs = [];
|
|
9515
|
+
const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
|
|
9516
|
+
const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
|
|
9517
|
+
0
|
|
9615
9518
|
);
|
|
9616
|
-
const
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
signingPublicKey,
|
|
9620
|
-
rawTx: newNodeTx.toBytes(),
|
|
9621
|
-
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
9622
|
-
};
|
|
9623
|
-
const newDirectNodeSigningJob = newDirectNodeTx ? {
|
|
9624
|
-
signingPublicKey,
|
|
9625
|
-
rawTx: newDirectNodeTx.toBytes(),
|
|
9626
|
-
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
9627
|
-
} : void 0;
|
|
9628
|
-
const newCpfpRefundSigningJob = {
|
|
9629
|
-
signingPublicKey,
|
|
9630
|
-
rawTx: newCpfpRefundTx.toBytes(),
|
|
9631
|
-
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
9519
|
+
const unsignedParentNodeOutput = {
|
|
9520
|
+
script: parentNodeOutput.script,
|
|
9521
|
+
amount: parentNodeOutput.amount
|
|
9632
9522
|
};
|
|
9633
|
-
const newDirectRefundSigningJob = newDirectRefundTx ? {
|
|
9634
|
-
signingPublicKey,
|
|
9635
|
-
rawTx: newDirectRefundTx.toBytes(),
|
|
9636
|
-
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
9637
|
-
} : void 0;
|
|
9638
|
-
const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
|
|
9639
|
-
signingPublicKey,
|
|
9640
|
-
rawTx: newDirectFromCpfpRefundTx.toBytes(),
|
|
9641
|
-
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
9642
|
-
} : void 0;
|
|
9643
|
-
const sparkClient = await this.connectionManager.createSparkClient(
|
|
9644
|
-
this.config.getCoordinatorAddress()
|
|
9645
|
-
);
|
|
9646
|
-
const response = await sparkClient.extend_leaf_v2({
|
|
9647
|
-
leafId: node.id,
|
|
9648
|
-
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
9649
|
-
nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
|
|
9650
|
-
directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
|
|
9651
|
-
refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
|
|
9652
|
-
directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
|
|
9653
|
-
directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
|
|
9654
|
-
});
|
|
9655
|
-
if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
|
|
9656
|
-
throw new Error("Signing result does not exist");
|
|
9657
|
-
}
|
|
9658
9523
|
const keyDerivation = {
|
|
9659
9524
|
type: "leaf" /* LEAF */,
|
|
9660
9525
|
path: node.id
|
|
9661
9526
|
};
|
|
9662
|
-
const
|
|
9663
|
-
|
|
9527
|
+
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
9528
|
+
const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
|
|
9529
|
+
signingJobs.push({
|
|
9530
|
+
signingPublicKey,
|
|
9531
|
+
rawTx: splitNodeTx.toBytes(),
|
|
9532
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9533
|
+
type: "split",
|
|
9534
|
+
parentTxOut: unsignedParentNodeOutput,
|
|
9535
|
+
leafId: node.id,
|
|
9664
9536
|
keyDerivation,
|
|
9665
|
-
|
|
9666
|
-
verifyingKey: response.nodeTxSigningResult.verifyingKey,
|
|
9667
|
-
selfCommitment: newNodeSigningJob.signingNonceCommitment,
|
|
9668
|
-
statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9669
|
-
adaptorPubKey: new Uint8Array()
|
|
9537
|
+
verifyingKey: node.verifyingPublicKey
|
|
9670
9538
|
});
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
selfSignature: nodeUserSig,
|
|
9680
|
-
adaptorPubKey: new Uint8Array()
|
|
9681
|
-
});
|
|
9682
|
-
let directNodeSig;
|
|
9683
|
-
if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
|
|
9684
|
-
const directNodeUserSig = await this.config.signer.signFrost({
|
|
9685
|
-
message: directNodeSighash,
|
|
9539
|
+
if (splitNodeDirectTx) {
|
|
9540
|
+
signingJobs.push({
|
|
9541
|
+
signingPublicKey,
|
|
9542
|
+
rawTx: splitNodeDirectTx.toBytes(),
|
|
9543
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9544
|
+
type: "directSplit",
|
|
9545
|
+
parentTxOut: unsignedParentNodeOutput,
|
|
9546
|
+
leafId: node.id,
|
|
9686
9547
|
keyDerivation,
|
|
9687
|
-
|
|
9688
|
-
verifyingKey: response.directNodeTxSigningResult.verifyingKey,
|
|
9689
|
-
selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
|
|
9690
|
-
statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9691
|
-
adaptorPubKey: new Uint8Array()
|
|
9692
|
-
});
|
|
9693
|
-
directNodeSig = await this.config.signer.aggregateFrost({
|
|
9694
|
-
message: directNodeSighash,
|
|
9695
|
-
statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
|
|
9696
|
-
statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
|
|
9697
|
-
verifyingKey: response.directNodeTxSigningResult.verifyingKey,
|
|
9698
|
-
statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9699
|
-
selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
|
|
9700
|
-
publicKey: signingPublicKey,
|
|
9701
|
-
selfSignature: directNodeUserSig,
|
|
9702
|
-
adaptorPubKey: new Uint8Array()
|
|
9548
|
+
verifyingKey: node.verifyingPublicKey
|
|
9703
9549
|
});
|
|
9704
9550
|
}
|
|
9705
|
-
const
|
|
9706
|
-
|
|
9551
|
+
const splitNodeOutput = splitNodeTx.getOutput(0);
|
|
9552
|
+
const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
|
|
9553
|
+
if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
|
|
9554
|
+
throw new Error("Could not get split node output");
|
|
9555
|
+
}
|
|
9556
|
+
const unsignedSplitNodeOutput = {
|
|
9557
|
+
script: splitNodeDirectOutput.script,
|
|
9558
|
+
amount: splitNodeDirectOutput.amount
|
|
9559
|
+
};
|
|
9560
|
+
const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
|
|
9561
|
+
signingJobs.push({
|
|
9562
|
+
signingPublicKey,
|
|
9563
|
+
rawTx: newNodeTx.toBytes(),
|
|
9564
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9565
|
+
type: "node",
|
|
9566
|
+
parentTxOut: splitNodeOutput,
|
|
9567
|
+
leafId: node.id,
|
|
9707
9568
|
keyDerivation,
|
|
9708
|
-
|
|
9709
|
-
verifyingKey: response.refundTxSigningResult.verifyingKey,
|
|
9710
|
-
selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
|
|
9711
|
-
statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9712
|
-
adaptorPubKey: new Uint8Array()
|
|
9569
|
+
verifyingKey: node.verifyingPublicKey
|
|
9713
9570
|
});
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
selfSignature: cpfpRefundUserSig,
|
|
9723
|
-
adaptorPubKey: new Uint8Array()
|
|
9724
|
-
});
|
|
9725
|
-
let directRefundSig;
|
|
9726
|
-
if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
|
|
9727
|
-
const directRefundUserSig = await this.config.signer.signFrost({
|
|
9728
|
-
message: directRefundSighash,
|
|
9729
|
-
keyDerivation,
|
|
9730
|
-
publicKey: signingPublicKey,
|
|
9731
|
-
verifyingKey: response.directRefundTxSigningResult.verifyingKey,
|
|
9732
|
-
selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
|
|
9733
|
-
statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9734
|
-
adaptorPubKey: new Uint8Array()
|
|
9735
|
-
});
|
|
9736
|
-
directRefundSig = await this.config.signer.aggregateFrost({
|
|
9737
|
-
message: directRefundSighash,
|
|
9738
|
-
statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
|
|
9739
|
-
statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
|
|
9740
|
-
verifyingKey: response.directRefundTxSigningResult.verifyingKey,
|
|
9741
|
-
statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9742
|
-
selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
|
|
9743
|
-
publicKey: signingPublicKey,
|
|
9744
|
-
selfSignature: directRefundUserSig,
|
|
9745
|
-
adaptorPubKey: new Uint8Array()
|
|
9746
|
-
});
|
|
9747
|
-
}
|
|
9748
|
-
let directFromCpfpRefundSig;
|
|
9749
|
-
if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
|
|
9750
|
-
const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
|
|
9751
|
-
message: directFromCpfpRefundSighash,
|
|
9571
|
+
if (newDirectNodeTx) {
|
|
9572
|
+
signingJobs.push({
|
|
9573
|
+
signingPublicKey,
|
|
9574
|
+
rawTx: newDirectNodeTx.toBytes(),
|
|
9575
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9576
|
+
type: "directNode",
|
|
9577
|
+
parentTxOut: splitNodeOutput,
|
|
9578
|
+
leafId: node.id,
|
|
9752
9579
|
keyDerivation,
|
|
9753
|
-
|
|
9754
|
-
verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
|
|
9755
|
-
selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
|
|
9756
|
-
statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9757
|
-
adaptorPubKey: new Uint8Array()
|
|
9758
|
-
});
|
|
9759
|
-
directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
|
|
9760
|
-
message: directFromCpfpRefundSighash,
|
|
9761
|
-
statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
|
|
9762
|
-
statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
|
|
9763
|
-
verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
|
|
9764
|
-
statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
9765
|
-
selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
|
|
9766
|
-
publicKey: signingPublicKey,
|
|
9767
|
-
selfSignature: directFromCpfpRefundUserSig,
|
|
9768
|
-
adaptorPubKey: new Uint8Array()
|
|
9580
|
+
verifyingKey: node.verifyingPublicKey
|
|
9769
9581
|
});
|
|
9770
9582
|
}
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
{
|
|
9775
|
-
nodeId: response.leafId,
|
|
9776
|
-
nodeTxSignature: nodeSig,
|
|
9777
|
-
directNodeTxSignature: directNodeSig,
|
|
9778
|
-
refundTxSignature: cpfpRefundSig,
|
|
9779
|
-
directRefundTxSignature: directRefundSig,
|
|
9780
|
-
directFromCpfpRefundTxSignature: directFromCpfpRefundSig
|
|
9781
|
-
}
|
|
9782
|
-
]
|
|
9783
|
-
});
|
|
9784
|
-
}
|
|
9785
|
-
async testonly_expireTimeLockNodeTx(node, parentNode) {
|
|
9786
|
-
return await this.refreshTimelockNodesInternal(node, parentNode, true);
|
|
9787
|
-
}
|
|
9788
|
-
async testonly_expireTimeLockRefundtx(node) {
|
|
9789
|
-
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
9790
|
-
const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
|
|
9791
|
-
const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
|
|
9792
|
-
const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
|
|
9793
|
-
const currTimelock = getCurrentTimelock(currSequence);
|
|
9794
|
-
if (currTimelock <= 100) {
|
|
9795
|
-
throw new ValidationError("Cannot expire timelock below 100", {
|
|
9796
|
-
field: "currTimelock",
|
|
9797
|
-
value: currTimelock,
|
|
9798
|
-
expected: "Timelock greater than 100"
|
|
9799
|
-
});
|
|
9800
|
-
}
|
|
9801
|
-
const nextSequence = TEST_UNILATERAL_SEQUENCE;
|
|
9802
|
-
const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
|
|
9803
|
-
const nodeOutput = nodeTx.getOutput(0);
|
|
9804
|
-
if (!nodeOutput) {
|
|
9805
|
-
throw Error("Could not get node output");
|
|
9806
|
-
}
|
|
9807
|
-
const keyDerivation = {
|
|
9808
|
-
type: "leaf" /* LEAF */,
|
|
9809
|
-
path: node.id
|
|
9810
|
-
};
|
|
9811
|
-
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
9812
|
-
const cpfpRefundOutPoint = {
|
|
9813
|
-
txid: hexToBytes9(getTxId(nodeTx)),
|
|
9814
|
-
index: 0
|
|
9815
|
-
};
|
|
9816
|
-
let directRefundOutPoint;
|
|
9817
|
-
if (directNodeTx) {
|
|
9818
|
-
directRefundOutPoint = {
|
|
9819
|
-
txid: hexToBytes9(getTxId(directNodeTx)),
|
|
9820
|
-
index: 0
|
|
9821
|
-
};
|
|
9583
|
+
const newCpfpNodeOutput = newNodeTx.getOutput(0);
|
|
9584
|
+
if (!newCpfpNodeOutput) {
|
|
9585
|
+
throw Error("Could not get new cpfp node output");
|
|
9822
9586
|
}
|
|
9587
|
+
const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
|
|
9823
9588
|
const {
|
|
9824
|
-
cpfpRefundTx:
|
|
9589
|
+
cpfpRefundTx: newRefundTx,
|
|
9825
9590
|
directRefundTx: newDirectRefundTx,
|
|
9826
9591
|
directFromCpfpRefundTx: newDirectFromCpfpRefundTx
|
|
9827
|
-
} =
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
input: cpfpRefundOutPoint,
|
|
9831
|
-
directInput: directRefundOutPoint,
|
|
9832
|
-
amountSats: nodeOutput.amount,
|
|
9592
|
+
} = createInitialTimelockRefundTxs({
|
|
9593
|
+
nodeTx: newNodeTx,
|
|
9594
|
+
directNodeTx: newDirectNodeTx,
|
|
9833
9595
|
receivingPubkey: signingPublicKey,
|
|
9834
9596
|
network: this.config.getNetwork()
|
|
9835
9597
|
});
|
|
9836
|
-
const signingJobs = [];
|
|
9837
9598
|
signingJobs.push({
|
|
9838
9599
|
signingPublicKey,
|
|
9839
|
-
rawTx:
|
|
9600
|
+
rawTx: newRefundTx.toBytes(),
|
|
9840
9601
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9841
9602
|
type: "cpfp",
|
|
9842
|
-
parentTxOut:
|
|
9603
|
+
parentTxOut: newCpfpNodeOutput,
|
|
9604
|
+
leafId: node.id,
|
|
9605
|
+
keyDerivation,
|
|
9606
|
+
verifyingKey: node.verifyingPublicKey
|
|
9843
9607
|
});
|
|
9844
|
-
|
|
9845
|
-
if (newDirectRefundTx && directNodeTxOut) {
|
|
9608
|
+
if (newDirectRefundTx && newDirectNodeOutput) {
|
|
9846
9609
|
signingJobs.push({
|
|
9847
9610
|
signingPublicKey,
|
|
9848
9611
|
rawTx: newDirectRefundTx.toBytes(),
|
|
9849
9612
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9850
9613
|
type: "direct",
|
|
9851
|
-
parentTxOut:
|
|
9614
|
+
parentTxOut: newDirectNodeOutput,
|
|
9615
|
+
leafId: node.id,
|
|
9616
|
+
keyDerivation,
|
|
9617
|
+
verifyingKey: node.verifyingPublicKey
|
|
9852
9618
|
});
|
|
9853
9619
|
}
|
|
9854
|
-
if (newDirectFromCpfpRefundTx) {
|
|
9620
|
+
if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
|
|
9855
9621
|
signingJobs.push({
|
|
9856
9622
|
signingPublicKey,
|
|
9857
9623
|
rawTx: newDirectFromCpfpRefundTx.toBytes(),
|
|
9858
9624
|
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9859
9625
|
type: "directFromCpfp",
|
|
9860
|
-
parentTxOut:
|
|
9626
|
+
parentTxOut: newCpfpNodeOutput,
|
|
9627
|
+
leafId: node.id,
|
|
9628
|
+
keyDerivation,
|
|
9629
|
+
verifyingKey: node.verifyingPublicKey
|
|
9861
9630
|
});
|
|
9862
9631
|
}
|
|
9632
|
+
return signingJobs;
|
|
9633
|
+
}
|
|
9634
|
+
async renewZeroTimelockNodeTxn(node) {
|
|
9863
9635
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
9864
9636
|
this.config.getCoordinatorAddress()
|
|
9865
9637
|
);
|
|
9866
|
-
const
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9638
|
+
const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
|
|
9639
|
+
const statechainCommitments = await sparkClient.get_signing_commitments({
|
|
9640
|
+
nodeIds: [node.id],
|
|
9641
|
+
count: signingJobs.length
|
|
9642
|
+
});
|
|
9643
|
+
const mappedSigningJobs = signingJobs.map((signingJob, index) => {
|
|
9644
|
+
const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
|
|
9645
|
+
if (!signingNonceCommitments) {
|
|
9646
|
+
throw new ValidationError("Signing nonce commitments not found", {
|
|
9647
|
+
field: "signingNonceCommitments",
|
|
9648
|
+
value: signingNonceCommitments
|
|
9649
|
+
});
|
|
9650
|
+
}
|
|
9651
|
+
return {
|
|
9652
|
+
...signingJob,
|
|
9653
|
+
signingNonceCommitments
|
|
9654
|
+
};
|
|
9870
9655
|
});
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
)
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9656
|
+
const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
|
|
9657
|
+
const renewZeroTimelockNodeSigningJob = {
|
|
9658
|
+
nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
|
|
9659
|
+
refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
|
|
9660
|
+
directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
|
|
9661
|
+
directRefundTxSigningJob: void 0,
|
|
9662
|
+
directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
|
|
9663
|
+
};
|
|
9664
|
+
const response = await sparkClient.renew_leaf({
|
|
9665
|
+
leafId: node.id,
|
|
9666
|
+
signingJobs: {
|
|
9667
|
+
$case: "renewNodeZeroTimelockSigningJob",
|
|
9668
|
+
renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
|
|
9883
9669
|
}
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
keyDerivation,
|
|
9890
|
-
publicKey: signingPublicKey,
|
|
9891
|
-
verifyingKey: signingResult.verifyingKey,
|
|
9892
|
-
selfCommitment: signingJob.signingNonceCommitment,
|
|
9893
|
-
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
9894
|
-
adaptorPubKey: new Uint8Array()
|
|
9895
|
-
});
|
|
9896
|
-
const signature = await this.config.signer.aggregateFrost({
|
|
9897
|
-
message: rawTxSighash,
|
|
9898
|
-
statechainSignatures: signingResult.signingResult?.signatureShares,
|
|
9899
|
-
statechainPublicKeys: signingResult.signingResult?.publicKeys,
|
|
9900
|
-
verifyingKey: signingResult.verifyingKey,
|
|
9901
|
-
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
9902
|
-
selfCommitment: signingJob.signingNonceCommitment,
|
|
9903
|
-
publicKey: signingPublicKey,
|
|
9904
|
-
selfSignature: userSignature,
|
|
9905
|
-
adaptorPubKey: new Uint8Array()
|
|
9670
|
+
});
|
|
9671
|
+
if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
|
|
9672
|
+
throw new ValidationError("Unexpected renew result", {
|
|
9673
|
+
field: "renewResult",
|
|
9674
|
+
value: response.renewResult
|
|
9906
9675
|
});
|
|
9907
|
-
if (signingJob.type === "cpfp") {
|
|
9908
|
-
cpfpRefundSignature = signature;
|
|
9909
|
-
} else if (signingJob.type === "direct") {
|
|
9910
|
-
directRefundSignature = signature;
|
|
9911
|
-
} else if (signingJob.type === "directFromCpfp") {
|
|
9912
|
-
directFromCpfpRefundSignature = signature;
|
|
9913
|
-
}
|
|
9914
9676
|
}
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9677
|
+
return response.renewResult.renewNodeZeroTimelockResult.node;
|
|
9678
|
+
}
|
|
9679
|
+
async createRenewZeroTimelockNodeSigningJobs(node) {
|
|
9680
|
+
const signingJobs = [];
|
|
9681
|
+
const keyDerivation = {
|
|
9682
|
+
type: "leaf" /* LEAF */,
|
|
9683
|
+
path: node.id
|
|
9684
|
+
};
|
|
9685
|
+
const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
9686
|
+
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
9687
|
+
const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
|
|
9688
|
+
signingJobs.push({
|
|
9689
|
+
signingPublicKey,
|
|
9690
|
+
rawTx: newNodeTx.toBytes(),
|
|
9691
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9692
|
+
type: "node",
|
|
9693
|
+
parentTxOut: nodeTx.getOutput(0),
|
|
9694
|
+
leafId: node.id,
|
|
9695
|
+
keyDerivation,
|
|
9696
|
+
verifyingKey: node.verifyingPublicKey
|
|
9927
9697
|
});
|
|
9928
|
-
|
|
9698
|
+
signingJobs.push({
|
|
9699
|
+
signingPublicKey,
|
|
9700
|
+
rawTx: newDirectNodeTx.toBytes(),
|
|
9701
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9702
|
+
type: "directNode",
|
|
9703
|
+
parentTxOut: nodeTx.getOutput(0),
|
|
9704
|
+
leafId: node.id,
|
|
9705
|
+
keyDerivation,
|
|
9706
|
+
verifyingKey: node.verifyingPublicKey
|
|
9707
|
+
});
|
|
9708
|
+
const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
|
|
9709
|
+
nodeTx: newNodeTx,
|
|
9710
|
+
directNodeTx: newDirectNodeTx,
|
|
9711
|
+
receivingPubkey: signingPublicKey,
|
|
9712
|
+
network: this.config.getNetwork()
|
|
9713
|
+
});
|
|
9714
|
+
signingJobs.push({
|
|
9715
|
+
signingPublicKey,
|
|
9716
|
+
rawTx: cpfpRefundTx.toBytes(),
|
|
9717
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9718
|
+
type: "cpfp",
|
|
9719
|
+
parentTxOut: newNodeTx.getOutput(0),
|
|
9720
|
+
leafId: node.id,
|
|
9721
|
+
keyDerivation,
|
|
9722
|
+
verifyingKey: node.verifyingPublicKey
|
|
9723
|
+
});
|
|
9724
|
+
if (!directFromCpfpRefundTx) {
|
|
9725
|
+
throw new Error("Could not create direct refund transactions");
|
|
9726
|
+
}
|
|
9727
|
+
signingJobs.push({
|
|
9728
|
+
signingPublicKey,
|
|
9729
|
+
rawTx: directFromCpfpRefundTx.toBytes(),
|
|
9730
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
|
|
9731
|
+
type: "directFromCpfp",
|
|
9732
|
+
parentTxOut: newNodeTx.getOutput(0),
|
|
9733
|
+
leafId: node.id,
|
|
9734
|
+
keyDerivation,
|
|
9735
|
+
verifyingKey: node.verifyingPublicKey
|
|
9736
|
+
});
|
|
9737
|
+
return signingJobs;
|
|
9929
9738
|
}
|
|
9930
9739
|
};
|
|
9931
9740
|
|
|
@@ -9967,71 +9776,6 @@ var CoopExitService = class extends BaseTransferService {
|
|
|
9967
9776
|
directFromCpfpSignaturesMap
|
|
9968
9777
|
};
|
|
9969
9778
|
}
|
|
9970
|
-
createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
|
|
9971
|
-
const cpfpRefundTx = new Transaction7();
|
|
9972
|
-
if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
|
|
9973
|
-
throw new ValidationError("Invalid CPFP node outpoint", {
|
|
9974
|
-
field: "cpfpNodeOutPoint",
|
|
9975
|
-
value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
|
|
9976
|
-
expected: "Both txid and index must be defined"
|
|
9977
|
-
});
|
|
9978
|
-
}
|
|
9979
|
-
cpfpRefundTx.addInput({
|
|
9980
|
-
txid: cpfpNodeOutPoint.txid,
|
|
9981
|
-
index: cpfpNodeOutPoint.index,
|
|
9982
|
-
sequence
|
|
9983
|
-
});
|
|
9984
|
-
cpfpRefundTx.addInput(connectorOutput);
|
|
9985
|
-
const receiverScript = getP2TRScriptFromPublicKey(
|
|
9986
|
-
receiverPubKey,
|
|
9987
|
-
this.config.getNetwork()
|
|
9988
|
-
);
|
|
9989
|
-
cpfpRefundTx.addOutput({
|
|
9990
|
-
script: receiverScript,
|
|
9991
|
-
amount: amountSats
|
|
9992
|
-
});
|
|
9993
|
-
let directRefundTx;
|
|
9994
|
-
let directFromCpfpRefundTx;
|
|
9995
|
-
if (directNodeOutPoint) {
|
|
9996
|
-
if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
|
|
9997
|
-
throw new ValidationError("Invalid direct node outpoint", {
|
|
9998
|
-
field: "directNodeOutPoint",
|
|
9999
|
-
value: {
|
|
10000
|
-
txid: directNodeOutPoint.txid,
|
|
10001
|
-
index: directNodeOutPoint.index
|
|
10002
|
-
},
|
|
10003
|
-
expected: "Both txid and index must be defined"
|
|
10004
|
-
});
|
|
10005
|
-
}
|
|
10006
|
-
directRefundTx = new Transaction7();
|
|
10007
|
-
directRefundTx.addInput({
|
|
10008
|
-
txid: directNodeOutPoint.txid,
|
|
10009
|
-
index: directNodeOutPoint.index,
|
|
10010
|
-
sequence: directSequence
|
|
10011
|
-
});
|
|
10012
|
-
directRefundTx.addInput(connectorOutput);
|
|
10013
|
-
directRefundTx.addOutput({
|
|
10014
|
-
script: receiverScript,
|
|
10015
|
-
amount: maybeApplyFee(amountSats)
|
|
10016
|
-
});
|
|
10017
|
-
directFromCpfpRefundTx = new Transaction7();
|
|
10018
|
-
directFromCpfpRefundTx.addInput({
|
|
10019
|
-
txid: cpfpNodeOutPoint.txid,
|
|
10020
|
-
index: cpfpNodeOutPoint.index,
|
|
10021
|
-
sequence: directSequence
|
|
10022
|
-
});
|
|
10023
|
-
directFromCpfpRefundTx.addInput(connectorOutput);
|
|
10024
|
-
directFromCpfpRefundTx.addOutput({
|
|
10025
|
-
script: receiverScript,
|
|
10026
|
-
amount: maybeApplyFee(amountSats)
|
|
10027
|
-
});
|
|
10028
|
-
}
|
|
10029
|
-
return {
|
|
10030
|
-
cpfpRefundTx,
|
|
10031
|
-
directRefundTx,
|
|
10032
|
-
directFromCpfpRefundTx
|
|
10033
|
-
};
|
|
10034
|
-
}
|
|
10035
9779
|
async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
|
|
10036
9780
|
if (leaves.length !== connectorOutputs.length) {
|
|
10037
9781
|
throw new ValidationError(
|
|
@@ -10065,29 +9809,39 @@ var CoopExitService = class extends BaseTransferService {
|
|
|
10065
9809
|
expected: "Valid connector output"
|
|
10066
9810
|
});
|
|
10067
9811
|
}
|
|
9812
|
+
const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
|
|
9813
|
+
let directNodeTx;
|
|
9814
|
+
if (leaf.leaf.directTx.length > 0) {
|
|
9815
|
+
directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
|
|
9816
|
+
}
|
|
10068
9817
|
const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
|
|
10069
|
-
|
|
10070
|
-
|
|
9818
|
+
if (!currentRefundTx) {
|
|
9819
|
+
throw new ValidationError("Invalid refund transaction", {
|
|
9820
|
+
field: "currentRefundTx",
|
|
9821
|
+
value: currentRefundTx,
|
|
9822
|
+
expected: "Non-null refund transaction"
|
|
9823
|
+
});
|
|
9824
|
+
}
|
|
9825
|
+
const currentSequence = currentRefundTx.getInput(0).sequence;
|
|
9826
|
+
if (!currentSequence) {
|
|
10071
9827
|
throw new ValidationError("Invalid refund transaction", {
|
|
10072
9828
|
field: "sequence",
|
|
10073
9829
|
value: currentRefundTx.getInput(0),
|
|
10074
9830
|
expected: "Non-null sequence"
|
|
10075
9831
|
});
|
|
10076
9832
|
}
|
|
10077
|
-
const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
|
|
10078
9833
|
let currentDirectRefundTx;
|
|
10079
9834
|
if (leaf.leaf.directRefundTx.length > 0) {
|
|
10080
9835
|
currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
|
|
10081
9836
|
}
|
|
10082
|
-
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
currentDirectRefundTx?.getInput(0),
|
|
9837
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
|
|
9838
|
+
nodeTx,
|
|
9839
|
+
directNodeTx,
|
|
9840
|
+
sequence: currentSequence,
|
|
10087
9841
|
connectorOutput,
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
);
|
|
9842
|
+
receivingPubkey: receiverPubKey,
|
|
9843
|
+
network: this.config.getNetwork()
|
|
9844
|
+
});
|
|
10091
9845
|
const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
10092
9846
|
const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
10093
9847
|
const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
@@ -10568,12 +10322,9 @@ import {
|
|
|
10568
10322
|
import { EventEmitter } from "eventemitter3";
|
|
10569
10323
|
import { ClientError, Status } from "nice-grpc-common";
|
|
10570
10324
|
|
|
10571
|
-
// src/services/signing.ts
|
|
10572
|
-
import { hexToBytes as hexToBytes12 } from "@noble/curves/utils";
|
|
10573
|
-
|
|
10574
10325
|
// src/utils/htlc-transactions.ts
|
|
10575
10326
|
import {
|
|
10576
|
-
Transaction as
|
|
10327
|
+
Transaction as Transaction6,
|
|
10577
10328
|
Script,
|
|
10578
10329
|
taprootListToTree,
|
|
10579
10330
|
p2tr as p2tr3,
|
|
@@ -10661,7 +10412,7 @@ function createLightningHTLCTransaction({
|
|
|
10661
10412
|
txid: hexToBytes11(getTxId(nodeTx)),
|
|
10662
10413
|
index: 0
|
|
10663
10414
|
};
|
|
10664
|
-
const htlcTransaction = new
|
|
10415
|
+
const htlcTransaction = new Transaction6({
|
|
10665
10416
|
version: 3,
|
|
10666
10417
|
allowUnknownOutputs: true
|
|
10667
10418
|
});
|
|
@@ -10780,20 +10531,7 @@ var SigningService = class {
|
|
|
10780
10531
|
});
|
|
10781
10532
|
}
|
|
10782
10533
|
const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
|
|
10783
|
-
const cpfpNodeOutPoint = {
|
|
10784
|
-
txid: hexToBytes12(getTxId(nodeTx)),
|
|
10785
|
-
index: 0
|
|
10786
|
-
};
|
|
10787
10534
|
const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
|
|
10788
|
-
const sequence = currRefundTx.getInput(0).sequence;
|
|
10789
|
-
if (!sequence) {
|
|
10790
|
-
throw new ValidationError("Invalid refund transaction", {
|
|
10791
|
-
field: "sequence",
|
|
10792
|
-
value: currRefundTx.getInput(0),
|
|
10793
|
-
expected: "Non-null sequence"
|
|
10794
|
-
});
|
|
10795
|
-
}
|
|
10796
|
-
const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
|
|
10797
10535
|
const amountSats = currRefundTx.getOutput(0).amount;
|
|
10798
10536
|
if (amountSats === void 0) {
|
|
10799
10537
|
throw new ValidationError("Invalid refund transaction", {
|
|
@@ -10803,20 +10541,21 @@ var SigningService = class {
|
|
|
10803
10541
|
});
|
|
10804
10542
|
}
|
|
10805
10543
|
let directNodeTx;
|
|
10806
|
-
let directNodeOutPoint;
|
|
10807
10544
|
if (leaf.leaf.directTx.length > 0) {
|
|
10808
10545
|
directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
|
|
10809
|
-
directNodeOutPoint = {
|
|
10810
|
-
txid: hexToBytes12(getTxId(directNodeTx)),
|
|
10811
|
-
index: 0
|
|
10812
|
-
};
|
|
10813
10546
|
}
|
|
10814
|
-
const
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10547
|
+
const currentSequence = currRefundTx.getInput(0).sequence;
|
|
10548
|
+
if (!currentSequence) {
|
|
10549
|
+
throw new ValidationError("Invalid refund transaction", {
|
|
10550
|
+
field: "sequence",
|
|
10551
|
+
value: currRefundTx.getInput(0),
|
|
10552
|
+
expected: "Non-null sequence"
|
|
10553
|
+
});
|
|
10554
|
+
}
|
|
10555
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
|
|
10556
|
+
nodeTx,
|
|
10557
|
+
directNodeTx,
|
|
10558
|
+
sequence: currentSequence,
|
|
10820
10559
|
receivingPubkey: receiverIdentityPubkey,
|
|
10821
10560
|
network: this.config.getNetwork()
|
|
10822
10561
|
});
|
|
@@ -10832,7 +10571,8 @@ var SigningService = class {
|
|
|
10832
10571
|
cpfpSigningCommitments[i]?.signingNonceCommitments
|
|
10833
10572
|
);
|
|
10834
10573
|
cpfpLeafSigningJobs.push(...signingJobs);
|
|
10835
|
-
|
|
10574
|
+
const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
|
|
10575
|
+
if (directRefundTx && !isZeroNode) {
|
|
10836
10576
|
if (!directNodeTx) {
|
|
10837
10577
|
throw new ValidationError(
|
|
10838
10578
|
"Direct node transaction undefined while direct refund transaction is defined",
|
|
@@ -10857,16 +10597,6 @@ var SigningService = class {
|
|
|
10857
10597
|
directLeafSigningJobs.push(...signingJobs2);
|
|
10858
10598
|
}
|
|
10859
10599
|
if (directFromCpfpRefundTx) {
|
|
10860
|
-
if (!directNodeTx) {
|
|
10861
|
-
throw new ValidationError(
|
|
10862
|
-
"Direct node transaction undefined while direct from CPFP refund transaction is defined",
|
|
10863
|
-
{
|
|
10864
|
-
field: "directNodeTx",
|
|
10865
|
-
value: directNodeTx,
|
|
10866
|
-
expected: "Non-null direct node transaction"
|
|
10867
|
-
}
|
|
10868
|
-
);
|
|
10869
|
-
}
|
|
10870
10600
|
const refundSighash2 = getSigHashFromTx(
|
|
10871
10601
|
directFromCpfpRefundTx,
|
|
10872
10602
|
0,
|
|
@@ -10987,21 +10717,116 @@ var SigningService = class {
|
|
|
10987
10717
|
directFromCpfpLeafSigningJobs.push(...signingJobs2);
|
|
10988
10718
|
}
|
|
10989
10719
|
}
|
|
10990
|
-
return {
|
|
10991
|
-
cpfpLeafSigningJobs,
|
|
10992
|
-
directLeafSigningJobs,
|
|
10993
|
-
directFromCpfpLeafSigningJobs
|
|
10994
|
-
};
|
|
10995
|
-
}
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
10720
|
+
return {
|
|
10721
|
+
cpfpLeafSigningJobs,
|
|
10722
|
+
directLeafSigningJobs,
|
|
10723
|
+
directFromCpfpLeafSigningJobs
|
|
10724
|
+
};
|
|
10725
|
+
}
|
|
10726
|
+
async signSigningJobs(signingJobs) {
|
|
10727
|
+
const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
|
|
10728
|
+
for (const signingJob of signingJobs) {
|
|
10729
|
+
const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
|
|
10730
|
+
const txOut = signingJob.parentTxOut;
|
|
10731
|
+
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
10732
|
+
const userSignature = await this.config.signer.signFrost({
|
|
10733
|
+
message: rawTxSighash,
|
|
10734
|
+
keyDerivation: signingJob.keyDerivation,
|
|
10735
|
+
publicKey: signingJob.signingPublicKey,
|
|
10736
|
+
verifyingKey: signingJob.verifyingKey,
|
|
10737
|
+
selfCommitment: signingJob.signingNonceCommitment,
|
|
10738
|
+
statechainCommitments: signingJob.signingNonceCommitments,
|
|
10739
|
+
adaptorPubKey: new Uint8Array()
|
|
10740
|
+
});
|
|
10741
|
+
const userSignedTxSigningJob = {
|
|
10742
|
+
leafId: signingJob.leafId,
|
|
10743
|
+
signingPublicKey: signingJob.signingPublicKey,
|
|
10744
|
+
rawTx: rawTx.toBytes(),
|
|
10745
|
+
signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
|
|
10746
|
+
signingCommitments: {
|
|
10747
|
+
signingCommitments: signingJob.signingNonceCommitments
|
|
10748
|
+
},
|
|
10749
|
+
userSignature
|
|
10750
|
+
};
|
|
10751
|
+
userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
|
|
10752
|
+
}
|
|
10753
|
+
return userSignedTxSigningJobs;
|
|
10754
|
+
}
|
|
10755
|
+
};
|
|
10756
|
+
|
|
10757
|
+
// src/utils/chunkArray.ts
|
|
10758
|
+
function chunkArray(arr, size) {
|
|
10759
|
+
const chunks = [];
|
|
10760
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
10761
|
+
chunks.push(arr.slice(i, i + size));
|
|
10762
|
+
}
|
|
10763
|
+
return chunks;
|
|
10764
|
+
}
|
|
10765
|
+
|
|
10766
|
+
// src/utils/retry.ts
|
|
10767
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
10768
|
+
maxAttempts: 5,
|
|
10769
|
+
baseDelayMs: 1e3,
|
|
10770
|
+
maxDelayMs: 1e4,
|
|
10771
|
+
backoffFactor: 2
|
|
10772
|
+
};
|
|
10773
|
+
function calculateBackoffDelay(attempt, config) {
|
|
10774
|
+
const delay = config.baseDelayMs * Math.pow(config.backoffFactor, attempt - 1);
|
|
10775
|
+
return Math.min(delay, config.maxDelayMs);
|
|
10776
|
+
}
|
|
10777
|
+
async function withRetry(operation, options = {}) {
|
|
10778
|
+
const config = options.config ?? DEFAULT_RETRY_CONFIG;
|
|
10779
|
+
const callbacks = options.callbacks ?? {};
|
|
10780
|
+
const { fetchData, onRetry, onError, onMaxAttemptsReached, onStart } = callbacks;
|
|
10781
|
+
if (onStart) {
|
|
10782
|
+
await onStart();
|
|
10783
|
+
}
|
|
10784
|
+
let currentData = void 0;
|
|
10785
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
10786
|
+
try {
|
|
10787
|
+
if (attempt > 1 && fetchData) {
|
|
10788
|
+
const context = {
|
|
10789
|
+
attempt,
|
|
10790
|
+
maxAttempts: config.maxAttempts,
|
|
10791
|
+
error: new Error("Placeholder"),
|
|
10792
|
+
delayMs: calculateBackoffDelay(attempt, config),
|
|
10793
|
+
data: currentData
|
|
10794
|
+
};
|
|
10795
|
+
currentData = await fetchData(context);
|
|
10796
|
+
}
|
|
10797
|
+
return await operation(currentData);
|
|
10798
|
+
} catch (error) {
|
|
10799
|
+
const lastError = error instanceof Error ? error : new Error(String(error));
|
|
10800
|
+
const delayMs = calculateBackoffDelay(attempt, config);
|
|
10801
|
+
const context = {
|
|
10802
|
+
attempt,
|
|
10803
|
+
maxAttempts: config.maxAttempts,
|
|
10804
|
+
error: lastError,
|
|
10805
|
+
delayMs,
|
|
10806
|
+
data: currentData
|
|
10807
|
+
};
|
|
10808
|
+
if (onError) {
|
|
10809
|
+
const result = await onError(context);
|
|
10810
|
+
if (result) {
|
|
10811
|
+
return result;
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
if (attempt === config.maxAttempts) {
|
|
10815
|
+
if (onMaxAttemptsReached) {
|
|
10816
|
+
const result = await onMaxAttemptsReached(context);
|
|
10817
|
+
if (result) {
|
|
10818
|
+
return result;
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
throw lastError;
|
|
10822
|
+
}
|
|
10823
|
+
if (onRetry) {
|
|
10824
|
+
await onRetry(context);
|
|
10825
|
+
}
|
|
10826
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
10827
|
+
}
|
|
11003
10828
|
}
|
|
11004
|
-
|
|
10829
|
+
throw new Error("Unexpected retry loop exit");
|
|
11005
10830
|
}
|
|
11006
10831
|
|
|
11007
10832
|
// src/utils/optimize.ts
|
|
@@ -11020,6 +10845,34 @@ function sorted(arr) {
|
|
|
11020
10845
|
function equals(a, b) {
|
|
11021
10846
|
return a.length === b.length && a.every((val, index) => val === b[index]);
|
|
11022
10847
|
}
|
|
10848
|
+
function countOccurrences(arr) {
|
|
10849
|
+
const map = /* @__PURE__ */ new Map();
|
|
10850
|
+
for (const x of arr) {
|
|
10851
|
+
map.set(x, (map.get(x) ?? 0) + 1);
|
|
10852
|
+
}
|
|
10853
|
+
return map;
|
|
10854
|
+
}
|
|
10855
|
+
function subtractCounters(a, b) {
|
|
10856
|
+
const result = /* @__PURE__ */ new Map();
|
|
10857
|
+
for (const [key, value] of a.entries()) {
|
|
10858
|
+
const diff = value - (b.get(key) ?? 0);
|
|
10859
|
+
if (diff > 0) {
|
|
10860
|
+
result.set(key, diff);
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
return result;
|
|
10864
|
+
}
|
|
10865
|
+
function counterToFlatArray(counter) {
|
|
10866
|
+
const arr = [];
|
|
10867
|
+
for (const [k, v] of Array.from(counter.entries()).sort(
|
|
10868
|
+
(a, b) => a[0] - b[0]
|
|
10869
|
+
)) {
|
|
10870
|
+
for (let i = 0; i < v; i++) {
|
|
10871
|
+
arr.push(k);
|
|
10872
|
+
}
|
|
10873
|
+
}
|
|
10874
|
+
return arr;
|
|
10875
|
+
}
|
|
11023
10876
|
function greedyLeaves(amount) {
|
|
11024
10877
|
const leaves = [];
|
|
11025
10878
|
let remaining = amount;
|
|
@@ -11035,6 +10888,24 @@ function greedyLeaves(amount) {
|
|
|
11035
10888
|
assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
|
|
11036
10889
|
return sorted(leaves);
|
|
11037
10890
|
}
|
|
10891
|
+
function swapMinimizingLeaves(amount, multiplicity = 1) {
|
|
10892
|
+
const leaves = [];
|
|
10893
|
+
let remaining = amount;
|
|
10894
|
+
assert(multiplicity > 0, "multiplicity must be > 0");
|
|
10895
|
+
for (const leaf of DENOMINATIONS) {
|
|
10896
|
+
if (typeof leaf === "number" && leaf > 0) {
|
|
10897
|
+
for (let i = 0; i < multiplicity; i++) {
|
|
10898
|
+
if (remaining >= leaf) {
|
|
10899
|
+
remaining -= leaf;
|
|
10900
|
+
leaves.push(leaf);
|
|
10901
|
+
}
|
|
10902
|
+
}
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10905
|
+
leaves.push(...greedyLeaves(remaining));
|
|
10906
|
+
assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
|
|
10907
|
+
return sorted(leaves);
|
|
10908
|
+
}
|
|
11038
10909
|
var Swap = class {
|
|
11039
10910
|
inLeaves;
|
|
11040
10911
|
outLeaves;
|
|
@@ -11072,71 +10943,83 @@ function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
|
|
|
11072
10943
|
}
|
|
11073
10944
|
return swaps;
|
|
11074
10945
|
}
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
const
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
error: lastError,
|
|
11115
|
-
delayMs,
|
|
11116
|
-
data: currentData
|
|
11117
|
-
};
|
|
11118
|
-
if (onError) {
|
|
11119
|
-
const result = await onError(context);
|
|
11120
|
-
if (result) {
|
|
11121
|
-
return result;
|
|
11122
|
-
}
|
|
11123
|
-
}
|
|
11124
|
-
if (attempt === config.maxAttempts) {
|
|
11125
|
-
if (onMaxAttemptsReached) {
|
|
11126
|
-
const result = await onMaxAttemptsReached(context);
|
|
11127
|
-
if (result) {
|
|
11128
|
-
return result;
|
|
10946
|
+
function minimizeTransferSwap(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
|
|
10947
|
+
const balance = sum(inputLeaves);
|
|
10948
|
+
const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
|
|
10949
|
+
const walletCounter = countOccurrences(inputLeaves);
|
|
10950
|
+
const optimalCounter = countOccurrences(optimalLeaves);
|
|
10951
|
+
const leavesToGive = subtractCounters(walletCounter, optimalCounter);
|
|
10952
|
+
const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
|
|
10953
|
+
const leavesToGiveFlat = counterToFlatArray(leavesToGive);
|
|
10954
|
+
const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
|
|
10955
|
+
const swaps = [];
|
|
10956
|
+
let toGiveBatch = [];
|
|
10957
|
+
let toReceiveBatch = [];
|
|
10958
|
+
let give = [...leavesToGiveFlat];
|
|
10959
|
+
let receive = [...leavesToReceiveFlat];
|
|
10960
|
+
while (give.length > 0 || receive.length > 0) {
|
|
10961
|
+
if (sum(toGiveBatch) > sum(toReceiveBatch)) {
|
|
10962
|
+
if (receive.length === 0) break;
|
|
10963
|
+
toReceiveBatch.push(receive.shift());
|
|
10964
|
+
} else {
|
|
10965
|
+
if (give.length === 0) break;
|
|
10966
|
+
toGiveBatch.push(give.shift());
|
|
10967
|
+
}
|
|
10968
|
+
if (toGiveBatch.length > 0 && toReceiveBatch.length > 0 && sum(toGiveBatch) === sum(toReceiveBatch)) {
|
|
10969
|
+
if (toGiveBatch.length > maxLeavesPerSwap) {
|
|
10970
|
+
for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
|
|
10971
|
+
const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
|
|
10972
|
+
swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
|
|
10973
|
+
}
|
|
10974
|
+
} else if (toReceiveBatch.length > maxLeavesPerSwap) {
|
|
10975
|
+
for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
|
|
10976
|
+
const sumCut = sum(toReceiveBatch.slice(0, cutoff));
|
|
10977
|
+
const remainder = sum(toGiveBatch) - sumCut;
|
|
10978
|
+
const alternateBatch = [
|
|
10979
|
+
...toReceiveBatch.slice(0, cutoff),
|
|
10980
|
+
...greedyLeaves(remainder)
|
|
10981
|
+
];
|
|
10982
|
+
if (alternateBatch.length <= maxLeavesPerSwap) {
|
|
10983
|
+
swaps.push(new Swap([...toGiveBatch], alternateBatch));
|
|
10984
|
+
break;
|
|
11129
10985
|
}
|
|
11130
10986
|
}
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
if (onRetry) {
|
|
11134
|
-
await onRetry(context);
|
|
10987
|
+
} else {
|
|
10988
|
+
swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
|
|
11135
10989
|
}
|
|
11136
|
-
|
|
10990
|
+
toGiveBatch = [];
|
|
10991
|
+
toReceiveBatch = [];
|
|
11137
10992
|
}
|
|
11138
10993
|
}
|
|
11139
|
-
|
|
10994
|
+
return swaps;
|
|
10995
|
+
}
|
|
10996
|
+
function shouldOptimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
|
|
10997
|
+
if (multiplicity == 0) {
|
|
10998
|
+
const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
|
|
10999
|
+
const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
|
|
11000
|
+
const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
|
|
11001
|
+
return numOutputs * 5 < numInputs;
|
|
11002
|
+
} else {
|
|
11003
|
+
const swaps = minimizeTransferSwap(
|
|
11004
|
+
inputLeaves,
|
|
11005
|
+
multiplicity,
|
|
11006
|
+
maxLeavesPerSwap
|
|
11007
|
+
);
|
|
11008
|
+
const inputCounter = countOccurrences(
|
|
11009
|
+
swaps.flatMap((swap) => swap.inLeaves)
|
|
11010
|
+
);
|
|
11011
|
+
const outputCounter = countOccurrences(
|
|
11012
|
+
swaps.flatMap((swap) => swap.outLeaves)
|
|
11013
|
+
);
|
|
11014
|
+
return Math.abs(inputCounter.size - outputCounter.size) > 1;
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
function optimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
|
|
11018
|
+
if (multiplicity == 0) {
|
|
11019
|
+
return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
|
|
11020
|
+
} else {
|
|
11021
|
+
return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
|
|
11022
|
+
}
|
|
11140
11023
|
}
|
|
11141
11024
|
|
|
11142
11025
|
// src/spark-wallet/spark-wallet.ts
|
|
@@ -11243,16 +11126,13 @@ var SparkWallet = class extends EventEmitter {
|
|
|
11243
11126
|
if (event.transfer.transfer && !equalBytes6(senderIdentityPublicKey, receiverIdentityPublicKey)) {
|
|
11244
11127
|
await this.claimTransfer({
|
|
11245
11128
|
transfer: event.transfer.transfer,
|
|
11246
|
-
emit: true
|
|
11247
|
-
optimize: true
|
|
11129
|
+
emit: true
|
|
11248
11130
|
});
|
|
11249
11131
|
}
|
|
11250
11132
|
} else if (isDepositStreamEvent(event)) {
|
|
11251
11133
|
const deposit = event.deposit.deposit;
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
type: "leaf" /* LEAF */,
|
|
11255
|
-
path: deposit.id
|
|
11134
|
+
await this.withLeaves(async () => {
|
|
11135
|
+
this.leaves.push(deposit);
|
|
11256
11136
|
});
|
|
11257
11137
|
this.emit(
|
|
11258
11138
|
SparkWalletEvent.DepositConfirmed,
|
|
@@ -11428,19 +11308,6 @@ var SparkWallet = class extends EventEmitter {
|
|
|
11428
11308
|
}
|
|
11429
11309
|
return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
|
|
11430
11310
|
}
|
|
11431
|
-
async checkExtendLeaves(leaves) {
|
|
11432
|
-
await this.withLeaves(async () => {
|
|
11433
|
-
for (const leaf of leaves) {
|
|
11434
|
-
if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
|
|
11435
|
-
const res = await this.transferService.extendTimelock(leaf);
|
|
11436
|
-
await this.transferLeavesToSelf(res.nodes, {
|
|
11437
|
-
type: "leaf" /* LEAF */,
|
|
11438
|
-
path: leaf.id
|
|
11439
|
-
});
|
|
11440
|
-
}
|
|
11441
|
-
}
|
|
11442
|
-
});
|
|
11443
|
-
}
|
|
11444
11311
|
verifyKey(pubkey1, pubkey2, verifyingKey) {
|
|
11445
11312
|
return equalBytes6(addPublicKeys(pubkey1, pubkey2), verifyingKey);
|
|
11446
11313
|
}
|
|
@@ -11550,75 +11417,84 @@ var SparkWallet = class extends EventEmitter {
|
|
|
11550
11417
|
}
|
|
11551
11418
|
return nodes;
|
|
11552
11419
|
}
|
|
11553
|
-
|
|
11554
|
-
const
|
|
11555
|
-
if (
|
|
11556
|
-
|
|
11557
|
-
}
|
|
11558
|
-
|
|
11559
|
-
let remainingAmount = totalAmount;
|
|
11560
|
-
let optimalLeavesLength = 0;
|
|
11561
|
-
for (let i = nextLowerPowerOfTwo; i >= 0; i--) {
|
|
11562
|
-
const denomination = 2 ** i;
|
|
11563
|
-
while (remainingAmount >= denomination) {
|
|
11564
|
-
remainingAmount -= denomination;
|
|
11565
|
-
optimalLeavesLength++;
|
|
11566
|
-
}
|
|
11420
|
+
async *optimizeLeaves(multiplicity = void 0) {
|
|
11421
|
+
const multiplicityValue = multiplicity ?? this.config.getOptimizationOptions().multiplicity ?? 0;
|
|
11422
|
+
if (multiplicityValue < 0) {
|
|
11423
|
+
throw new ValidationError("Multiplicity cannot be negative");
|
|
11424
|
+
} else if (multiplicityValue > 5) {
|
|
11425
|
+
throw new ValidationError("Multiplicity cannot be greater than 5");
|
|
11567
11426
|
}
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11427
|
+
if (this.optimizationInProgress || !shouldOptimize(
|
|
11428
|
+
this.leaves.map((leaf) => leaf.value),
|
|
11429
|
+
multiplicityValue
|
|
11430
|
+
)) {
|
|
11572
11431
|
return;
|
|
11573
11432
|
}
|
|
11574
|
-
|
|
11433
|
+
const controller = new AbortController();
|
|
11434
|
+
const release = await this.leavesMutex.acquire();
|
|
11435
|
+
try {
|
|
11575
11436
|
this.optimizationInProgress = true;
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11437
|
+
this.leaves = await this.getLeaves();
|
|
11438
|
+
const swaps = optimize(
|
|
11439
|
+
this.leaves.map((leaf) => leaf.value),
|
|
11440
|
+
multiplicityValue
|
|
11441
|
+
);
|
|
11442
|
+
if (swaps.length === 0) {
|
|
11443
|
+
return;
|
|
11444
|
+
}
|
|
11445
|
+
yield {
|
|
11446
|
+
step: 0,
|
|
11447
|
+
total: swaps.length,
|
|
11448
|
+
controller
|
|
11449
|
+
};
|
|
11450
|
+
const valueToNodes = /* @__PURE__ */ new Map();
|
|
11451
|
+
this.leaves.forEach((leaf) => {
|
|
11452
|
+
if (!valueToNodes.has(leaf.value)) {
|
|
11453
|
+
valueToNodes.set(leaf.value, []);
|
|
11454
|
+
}
|
|
11455
|
+
valueToNodes.get(leaf.value).push(leaf);
|
|
11456
|
+
});
|
|
11457
|
+
for (const swap of swaps) {
|
|
11458
|
+
if (controller.signal.aborted) {
|
|
11459
|
+
break;
|
|
11460
|
+
}
|
|
11461
|
+
const leavesToSend = [];
|
|
11462
|
+
for (const leafValue of swap.inLeaves) {
|
|
11463
|
+
const nodes = valueToNodes.get(leafValue);
|
|
11464
|
+
if (nodes && nodes.length > 0) {
|
|
11465
|
+
const node = nodes.shift();
|
|
11466
|
+
leavesToSend.push(node);
|
|
11467
|
+
} else {
|
|
11468
|
+
throw new InternalValidationError(
|
|
11469
|
+
`No unused leaf with value ${leafValue} found in leaves`
|
|
11470
|
+
);
|
|
11600
11471
|
}
|
|
11601
|
-
await this.requestLeavesSwap({
|
|
11602
|
-
leaves: leavesToSend,
|
|
11603
|
-
targetAmounts: swap.outLeaves
|
|
11604
|
-
});
|
|
11605
11472
|
}
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11473
|
+
await this.requestLeavesSwap({
|
|
11474
|
+
leaves: leavesToSend,
|
|
11475
|
+
targetAmounts: swap.outLeaves
|
|
11476
|
+
});
|
|
11477
|
+
yield {
|
|
11478
|
+
step: swaps.indexOf(swap) + 1,
|
|
11479
|
+
total: swaps.length,
|
|
11480
|
+
controller
|
|
11481
|
+
};
|
|
11609
11482
|
}
|
|
11610
|
-
|
|
11483
|
+
this.leaves = await this.getLeaves();
|
|
11484
|
+
} finally {
|
|
11485
|
+
this.optimizationInProgress = false;
|
|
11486
|
+
release();
|
|
11487
|
+
}
|
|
11611
11488
|
}
|
|
11612
11489
|
async syncWallet() {
|
|
11613
11490
|
await this.syncTokenOutputs();
|
|
11614
11491
|
let leaves = await this.getLeaves();
|
|
11615
|
-
leaves = await this.
|
|
11616
|
-
leaves = await this.checkExtendTimeLockNodes(leaves);
|
|
11492
|
+
leaves = await this.checkRenewLeaves(leaves);
|
|
11617
11493
|
this.leaves = leaves;
|
|
11618
|
-
this.
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
}
|
|
11494
|
+
if (this.config.getOptimizationOptions().auto) {
|
|
11495
|
+
for await (const _ of this.optimizeLeaves()) {
|
|
11496
|
+
}
|
|
11497
|
+
}
|
|
11622
11498
|
}
|
|
11623
11499
|
async withLeaves(operation) {
|
|
11624
11500
|
const release = await this.leavesMutex.acquire();
|
|
@@ -11735,7 +11611,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
11735
11611
|
mnemonic = mnemonicOrSeed;
|
|
11736
11612
|
seed = await this.config.signer.mnemonicToSeed(mnemonicOrSeed);
|
|
11737
11613
|
} else {
|
|
11738
|
-
seed =
|
|
11614
|
+
seed = hexToBytes12(mnemonicOrSeed);
|
|
11739
11615
|
}
|
|
11740
11616
|
}
|
|
11741
11617
|
await this.initWalletFromSeed(seed, accountNumber);
|
|
@@ -11878,7 +11754,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
11878
11754
|
directFromCpfpSignatureMap
|
|
11879
11755
|
} = await this.transferService.startSwapSignRefund(
|
|
11880
11756
|
leafKeyTweaks,
|
|
11881
|
-
|
|
11757
|
+
hexToBytes12(this.config.getSspIdentityPublicKey()),
|
|
11882
11758
|
new Date(Date.now() + 2 * 60 * 1e3)
|
|
11883
11759
|
);
|
|
11884
11760
|
try {
|
|
@@ -12093,7 +11969,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12093
11969
|
throw new Error(`Leaf not found for node ${nodeId}`);
|
|
12094
11970
|
}
|
|
12095
11971
|
const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
12096
|
-
const cpfpRefundTxBytes =
|
|
11972
|
+
const cpfpRefundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
|
|
12097
11973
|
const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
|
|
12098
11974
|
const cpfpSighash = getSigHashFromTx(
|
|
12099
11975
|
cpfpRefundTx,
|
|
@@ -12102,7 +11978,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12102
11978
|
);
|
|
12103
11979
|
const nodePublicKey = node.verifyingPublicKey;
|
|
12104
11980
|
const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
|
|
12105
|
-
const cpfpAdaptorSignatureBytes =
|
|
11981
|
+
const cpfpAdaptorSignatureBytes = hexToBytes12(
|
|
12106
11982
|
leaf.adaptorSignedSignature
|
|
12107
11983
|
);
|
|
12108
11984
|
applyAdaptorToSignature(
|
|
@@ -12113,7 +11989,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12113
11989
|
);
|
|
12114
11990
|
if (leaf.directRawUnsignedRefundTransaction) {
|
|
12115
11991
|
const directNodeTx = getTxFromRawTxBytes(node.directTx);
|
|
12116
|
-
const directRefundTxBytes =
|
|
11992
|
+
const directRefundTxBytes = hexToBytes12(
|
|
12117
11993
|
leaf.directRawUnsignedRefundTransaction
|
|
12118
11994
|
);
|
|
12119
11995
|
const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
|
|
@@ -12127,7 +12003,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12127
12003
|
`Direct adaptor signed signature missing for node ${nodeId}`
|
|
12128
12004
|
);
|
|
12129
12005
|
}
|
|
12130
|
-
const directAdaptorSignatureBytes =
|
|
12006
|
+
const directAdaptorSignatureBytes = hexToBytes12(
|
|
12131
12007
|
leaf.directAdaptorSignedSignature
|
|
12132
12008
|
);
|
|
12133
12009
|
applyAdaptorToSignature(
|
|
@@ -12138,7 +12014,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12138
12014
|
);
|
|
12139
12015
|
}
|
|
12140
12016
|
if (leaf.directFromCpfpRawUnsignedRefundTransaction) {
|
|
12141
|
-
const directFromCpfpRefundTxBytes =
|
|
12017
|
+
const directFromCpfpRefundTxBytes = hexToBytes12(
|
|
12142
12018
|
leaf.directFromCpfpRawUnsignedRefundTransaction
|
|
12143
12019
|
);
|
|
12144
12020
|
const directFromCpfpRefundTx = getTxFromRawTxBytes(
|
|
@@ -12154,7 +12030,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12154
12030
|
`Direct adaptor signed signature missing for node ${nodeId}`
|
|
12155
12031
|
);
|
|
12156
12032
|
}
|
|
12157
|
-
const directFromCpfpAdaptorSignatureBytes =
|
|
12033
|
+
const directFromCpfpAdaptorSignatureBytes = hexToBytes12(
|
|
12158
12034
|
leaf.directFromCpfpAdaptorSignedSignature
|
|
12159
12035
|
);
|
|
12160
12036
|
applyAdaptorToSignature(
|
|
@@ -12201,8 +12077,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12201
12077
|
}
|
|
12202
12078
|
return await this.claimTransfer({
|
|
12203
12079
|
transfer: incomingTransfer,
|
|
12204
|
-
emit: false
|
|
12205
|
-
optimize: false
|
|
12080
|
+
emit: false
|
|
12206
12081
|
});
|
|
12207
12082
|
} catch (e) {
|
|
12208
12083
|
console.error("[processSwapBatch] Error details:", {
|
|
@@ -12596,7 +12471,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12596
12471
|
}
|
|
12597
12472
|
);
|
|
12598
12473
|
}
|
|
12599
|
-
const tx = new
|
|
12474
|
+
const tx = new Transaction7();
|
|
12600
12475
|
tx.addInput({
|
|
12601
12476
|
txid: depositTransactionId,
|
|
12602
12477
|
index: outputIndex,
|
|
@@ -12636,7 +12511,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12636
12511
|
);
|
|
12637
12512
|
const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
|
|
12638
12513
|
onChainUtxo: {
|
|
12639
|
-
txid:
|
|
12514
|
+
txid: hexToBytes12(depositTransactionId),
|
|
12640
12515
|
vout: outputIndex,
|
|
12641
12516
|
network: networkType
|
|
12642
12517
|
},
|
|
@@ -12763,7 +12638,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12763
12638
|
creditAmountView.setUint32(0, lowerHalf, true);
|
|
12764
12639
|
creditAmountView.setUint32(4, upperHalf, true);
|
|
12765
12640
|
parts.push(new Uint8Array(creditAmountBuffer));
|
|
12766
|
-
parts.push(
|
|
12641
|
+
parts.push(hexToBytes12(sspSignature));
|
|
12767
12642
|
const totalLength = parts.reduce((sum2, part) => sum2 + part.length, 0);
|
|
12768
12643
|
const payload = new Uint8Array(totalLength);
|
|
12769
12644
|
let offset = 0;
|
|
@@ -12867,26 +12742,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
12867
12742
|
depositTx,
|
|
12868
12743
|
vout
|
|
12869
12744
|
});
|
|
12870
|
-
|
|
12871
|
-
for (const node of res.nodes) {
|
|
12872
|
-
if (node.status === "AVAILABLE") {
|
|
12873
|
-
const { nodes } = await this.transferService.extendTimelock(node);
|
|
12874
|
-
for (const n of nodes) {
|
|
12875
|
-
if (n.status === "AVAILABLE") {
|
|
12876
|
-
const transfer = await this.transferLeavesToSelf([n], {
|
|
12877
|
-
type: "leaf" /* LEAF */,
|
|
12878
|
-
path: node.id
|
|
12879
|
-
});
|
|
12880
|
-
resultingNodes.push(...transfer);
|
|
12881
|
-
} else {
|
|
12882
|
-
resultingNodes.push(n);
|
|
12883
|
-
}
|
|
12884
|
-
}
|
|
12885
|
-
} else {
|
|
12886
|
-
resultingNodes.push(node);
|
|
12887
|
-
}
|
|
12888
|
-
}
|
|
12889
|
-
return resultingNodes;
|
|
12745
|
+
return res.nodes;
|
|
12890
12746
|
}
|
|
12891
12747
|
/**
|
|
12892
12748
|
* Gets all unused deposit addresses for the wallet.
|
|
@@ -13028,6 +12884,9 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13028
12884
|
depositTx,
|
|
13029
12885
|
vout
|
|
13030
12886
|
});
|
|
12887
|
+
await this.withLeaves(async () => {
|
|
12888
|
+
this.leaves.push(...nodes2);
|
|
12889
|
+
});
|
|
13031
12890
|
return nodes2;
|
|
13032
12891
|
});
|
|
13033
12892
|
this.mutexes.delete(txid);
|
|
@@ -13157,7 +13016,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13157
13016
|
const [outcome] = await this.transferWithInvoice([
|
|
13158
13017
|
{
|
|
13159
13018
|
amountSats,
|
|
13160
|
-
receiverIdentityPubkey:
|
|
13019
|
+
receiverIdentityPubkey: hexToBytes12(receiverAddress.identityPublicKey)
|
|
13161
13020
|
}
|
|
13162
13021
|
]);
|
|
13163
13022
|
if (!outcome) throw new Error("no transfer created");
|
|
@@ -13204,8 +13063,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13204
13063
|
`TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
|
|
13205
13064
|
);
|
|
13206
13065
|
}
|
|
13207
|
-
|
|
13208
|
-
available = await this.checkExtendTimeLockNodes(available);
|
|
13066
|
+
const available = await this.checkRenewLeaves(group);
|
|
13209
13067
|
if (available.length < group.length) {
|
|
13210
13068
|
throw new Error(
|
|
13211
13069
|
`Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
|
|
@@ -13248,7 +13106,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13248
13106
|
transfer.id
|
|
13249
13107
|
);
|
|
13250
13108
|
if (pending) {
|
|
13251
|
-
await this.claimTransfer({ transfer: pending
|
|
13109
|
+
await this.claimTransfer({ transfer: pending });
|
|
13252
13110
|
}
|
|
13253
13111
|
}
|
|
13254
13112
|
return {
|
|
@@ -13298,75 +13156,45 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13298
13156
|
newKeyDerivation: { type: "random" /* RANDOM */ }
|
|
13299
13157
|
};
|
|
13300
13158
|
}
|
|
13301
|
-
async
|
|
13302
|
-
const
|
|
13159
|
+
async checkRenewLeaves(nodes) {
|
|
13160
|
+
const nodesToRenewNode = [];
|
|
13161
|
+
const nodesToRenewRefund = [];
|
|
13162
|
+
const nodesToRenewZeroTimelock = [];
|
|
13303
13163
|
const nodeIds = [];
|
|
13304
13164
|
const validNodes = [];
|
|
13305
13165
|
for (const node of nodes) {
|
|
13306
13166
|
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
13307
|
-
const
|
|
13308
|
-
|
|
13167
|
+
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
13168
|
+
const nodeSequence = nodeTx.getInput(0).sequence;
|
|
13169
|
+
const refundSequence = refundTx.getInput(0).sequence;
|
|
13170
|
+
if (nodeSequence === void 0) {
|
|
13309
13171
|
throw new ValidationError("Invalid node transaction", {
|
|
13310
13172
|
field: "sequence",
|
|
13311
13173
|
value: nodeTx.getInput(0),
|
|
13312
13174
|
expected: "Non-null sequence"
|
|
13313
13175
|
});
|
|
13314
13176
|
}
|
|
13315
|
-
|
|
13316
|
-
if (needsRefresh) {
|
|
13317
|
-
nodesToExtend.push(node);
|
|
13318
|
-
nodeIds.push(node.id);
|
|
13319
|
-
} else {
|
|
13320
|
-
validNodes.push(node);
|
|
13321
|
-
}
|
|
13322
|
-
}
|
|
13323
|
-
if (nodesToExtend.length === 0) {
|
|
13324
|
-
return validNodes;
|
|
13325
|
-
}
|
|
13326
|
-
const nodesToAdd = [];
|
|
13327
|
-
for (const node of nodesToExtend) {
|
|
13328
|
-
const { nodes: nodes2 } = await this.transferService.extendTimelock(node);
|
|
13329
|
-
this.leaves = this.leaves.filter((leaf) => leaf.id !== node.id);
|
|
13330
|
-
const newNodes = await this.transferLeavesToSelf(nodes2, {
|
|
13331
|
-
type: "leaf" /* LEAF */,
|
|
13332
|
-
path: node.id
|
|
13333
|
-
});
|
|
13334
|
-
nodesToAdd.push(...newNodes);
|
|
13335
|
-
}
|
|
13336
|
-
this.updateLeaves(nodeIds, nodesToAdd);
|
|
13337
|
-
validNodes.push(...nodesToAdd);
|
|
13338
|
-
return validNodes;
|
|
13339
|
-
}
|
|
13340
|
-
/**
|
|
13341
|
-
* Internal method to refresh timelock nodes.
|
|
13342
|
-
*
|
|
13343
|
-
* @param {string} nodeId - The optional ID of the node to refresh. If not provided, all nodes will be checked.
|
|
13344
|
-
* @returns {Promise<void>}
|
|
13345
|
-
* @private
|
|
13346
|
-
*/
|
|
13347
|
-
async checkRefreshTimelockNodes(nodes) {
|
|
13348
|
-
const nodesToRefresh = [];
|
|
13349
|
-
const nodeIds = [];
|
|
13350
|
-
const validNodes = [];
|
|
13351
|
-
for (const node of nodes) {
|
|
13352
|
-
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
13353
|
-
const sequence = refundTx.getInput(0).sequence;
|
|
13354
|
-
if (!sequence) {
|
|
13177
|
+
if (!refundSequence) {
|
|
13355
13178
|
throw new ValidationError("Invalid refund transaction", {
|
|
13356
13179
|
field: "sequence",
|
|
13357
13180
|
value: refundTx.getInput(0),
|
|
13358
13181
|
expected: "Non-null sequence"
|
|
13359
13182
|
});
|
|
13360
13183
|
}
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13184
|
+
if (doesTxnNeedRenewed(refundSequence)) {
|
|
13185
|
+
if (isZeroTimelock(nodeSequence)) {
|
|
13186
|
+
nodesToRenewZeroTimelock.push(node);
|
|
13187
|
+
} else if (doesTxnNeedRenewed(nodeSequence)) {
|
|
13188
|
+
nodesToRenewNode.push(node);
|
|
13189
|
+
} else {
|
|
13190
|
+
nodesToRenewRefund.push(node);
|
|
13191
|
+
}
|
|
13364
13192
|
nodeIds.push(node.id);
|
|
13365
13193
|
} else {
|
|
13366
13194
|
validNodes.push(node);
|
|
13367
13195
|
}
|
|
13368
13196
|
}
|
|
13369
|
-
if (
|
|
13197
|
+
if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
|
|
13370
13198
|
return validNodes;
|
|
13371
13199
|
}
|
|
13372
13200
|
const nodesResp = await this.queryNodes({
|
|
@@ -13384,7 +13212,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13384
13212
|
nodesMap.set(node.id, node);
|
|
13385
13213
|
}
|
|
13386
13214
|
const nodesToAdd = [];
|
|
13387
|
-
for (const node of
|
|
13215
|
+
for (const node of nodesToRenewNode) {
|
|
13388
13216
|
if (!node.parentNodeId) {
|
|
13389
13217
|
throw new Error(`node ${node.id} has no parent`);
|
|
13390
13218
|
}
|
|
@@ -13392,17 +13220,25 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13392
13220
|
if (!parentNode) {
|
|
13393
13221
|
throw new Error(`parent node ${node.parentNodeId} not found`);
|
|
13394
13222
|
}
|
|
13395
|
-
const
|
|
13223
|
+
const newNode = await this.transferService.renewNodeTxn(node, parentNode);
|
|
13224
|
+
nodesToAdd.push(newNode);
|
|
13225
|
+
}
|
|
13226
|
+
for (const node of nodesToRenewRefund) {
|
|
13227
|
+
if (!node.parentNodeId) {
|
|
13228
|
+
throw new Error(`node ${node.id} has no parent`);
|
|
13229
|
+
}
|
|
13230
|
+
const parentNode = nodesMap.get(node.parentNodeId);
|
|
13231
|
+
if (!parentNode) {
|
|
13232
|
+
throw new Error(`parent node ${node.parentNodeId} not found`);
|
|
13233
|
+
}
|
|
13234
|
+
const newNode = await this.transferService.renewRefundTxn(
|
|
13396
13235
|
node,
|
|
13397
13236
|
parentNode
|
|
13398
13237
|
);
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
const newNode =
|
|
13403
|
-
if (!newNode) {
|
|
13404
|
-
throw new Error("Failed to refresh timelock node");
|
|
13405
|
-
}
|
|
13238
|
+
nodesToAdd.push(newNode);
|
|
13239
|
+
}
|
|
13240
|
+
for (const node of nodesToRenewZeroTimelock) {
|
|
13241
|
+
const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
|
|
13406
13242
|
nodesToAdd.push(newNode);
|
|
13407
13243
|
}
|
|
13408
13244
|
this.updateLeaves(nodeIds, nodesToAdd);
|
|
@@ -13443,14 +13279,14 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13443
13279
|
return response.nodes;
|
|
13444
13280
|
});
|
|
13445
13281
|
}
|
|
13446
|
-
async processClaimedTransferResults(result, transfer, emit
|
|
13447
|
-
result = await this.
|
|
13448
|
-
result = await this.checkExtendTimeLockNodes(result);
|
|
13282
|
+
async processClaimedTransferResults(result, transfer, emit) {
|
|
13283
|
+
result = await this.checkRenewLeaves(result);
|
|
13449
13284
|
const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
|
|
13450
13285
|
const uniqueResults = result.filter((node) => !existingIds.has(node.id));
|
|
13451
13286
|
this.leaves.push(...uniqueResults);
|
|
13452
|
-
if (
|
|
13453
|
-
await this.optimizeLeaves()
|
|
13287
|
+
if (this.config.getOptimizationOptions().auto && transfer.type !== 40 /* COUNTER_SWAP */) {
|
|
13288
|
+
for await (const _ of this.optimizeLeaves()) {
|
|
13289
|
+
}
|
|
13454
13290
|
}
|
|
13455
13291
|
if (emit) {
|
|
13456
13292
|
this.emit(
|
|
@@ -13469,8 +13305,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13469
13305
|
*/
|
|
13470
13306
|
async claimTransfer({
|
|
13471
13307
|
transfer,
|
|
13472
|
-
emit
|
|
13473
|
-
optimize
|
|
13308
|
+
emit
|
|
13474
13309
|
}) {
|
|
13475
13310
|
const onError = async (context) => {
|
|
13476
13311
|
const error = context.error;
|
|
@@ -13515,12 +13350,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13515
13350
|
if (result.length === 0) {
|
|
13516
13351
|
return [];
|
|
13517
13352
|
}
|
|
13518
|
-
return await this.processClaimedTransferResults(
|
|
13519
|
-
result,
|
|
13520
|
-
transfer,
|
|
13521
|
-
emit,
|
|
13522
|
-
optimize
|
|
13523
|
-
);
|
|
13353
|
+
return await this.processClaimedTransferResults(result, transfer, emit);
|
|
13524
13354
|
} catch (error) {
|
|
13525
13355
|
console.warn(
|
|
13526
13356
|
`Failed to claim transfer after all retries. Please try reinitializing your wallet in a few minutes. Transfer ID: ${transfer.id}`,
|
|
@@ -13553,7 +13383,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13553
13383
|
continue;
|
|
13554
13384
|
}
|
|
13555
13385
|
promises.push(
|
|
13556
|
-
this.claimTransfer({ transfer, emit
|
|
13386
|
+
this.claimTransfer({ transfer, emit }).then(() => transfer.id).catch((error) => {
|
|
13557
13387
|
console.warn(`Failed to claim transfer ${transfer.id}:`, error);
|
|
13558
13388
|
return null;
|
|
13559
13389
|
})
|
|
@@ -13779,7 +13609,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13779
13609
|
const sparkFallbackAddress = decodedInvoice.fallbackAddress;
|
|
13780
13610
|
const paymentHash = decodedInvoice.paymentHash;
|
|
13781
13611
|
if (preferSpark) {
|
|
13782
|
-
if (sparkFallbackAddress === void 0 || isValidSparkFallback(
|
|
13612
|
+
if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes12(sparkFallbackAddress)) === false) {
|
|
13783
13613
|
console.warn(
|
|
13784
13614
|
"No valid spark address found in invoice. Defaulting to lightning."
|
|
13785
13615
|
);
|
|
@@ -13824,8 +13654,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13824
13654
|
selectedLeaves,
|
|
13825
13655
|
`no leaves for ${totalAmount}`
|
|
13826
13656
|
);
|
|
13827
|
-
leaves = await this.
|
|
13828
|
-
leaves = await this.checkExtendTimeLockNodes(leaves);
|
|
13657
|
+
leaves = await this.checkRenewLeaves(leaves);
|
|
13829
13658
|
const leavesToSend = await Promise.all(
|
|
13830
13659
|
leaves.map(async (leaf) => ({
|
|
13831
13660
|
leaf,
|
|
@@ -13841,17 +13670,17 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13841
13670
|
const transferID = uuidv74();
|
|
13842
13671
|
const startTransferRequest = await this.transferService.prepareTransferForLightning(
|
|
13843
13672
|
leavesToSend,
|
|
13844
|
-
|
|
13845
|
-
|
|
13673
|
+
hexToBytes12(this.config.getSspIdentityPublicKey()),
|
|
13674
|
+
hexToBytes12(paymentHash),
|
|
13846
13675
|
expiryTime,
|
|
13847
13676
|
transferID
|
|
13848
13677
|
);
|
|
13849
13678
|
const swapResponse = await this.lightningService.swapNodesForPreimage({
|
|
13850
13679
|
leaves: leavesToSend,
|
|
13851
|
-
receiverIdentityPubkey:
|
|
13680
|
+
receiverIdentityPubkey: hexToBytes12(
|
|
13852
13681
|
this.config.getSspIdentityPublicKey()
|
|
13853
13682
|
),
|
|
13854
|
-
paymentHash:
|
|
13683
|
+
paymentHash: hexToBytes12(paymentHash),
|
|
13855
13684
|
isInboundPayment: false,
|
|
13856
13685
|
invoiceString: invoice,
|
|
13857
13686
|
feeSats: feeEstimate,
|
|
@@ -13954,7 +13783,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
13954
13783
|
}
|
|
13955
13784
|
satsInvoices.push({
|
|
13956
13785
|
amountSats: encodedAmount ?? Number(amount),
|
|
13957
|
-
receiverIdentityPubkey:
|
|
13786
|
+
receiverIdentityPubkey: hexToBytes12(addressData.identityPublicKey),
|
|
13958
13787
|
sparkInvoice: invoice
|
|
13959
13788
|
});
|
|
13960
13789
|
} else if (fields.paymentType?.type === "tokens") {
|
|
@@ -14156,10 +13985,8 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14156
13985
|
leavesToSendToSsp = leavesForTargetAmount;
|
|
14157
13986
|
leavesToSendToSE = leavesForFee;
|
|
14158
13987
|
}
|
|
14159
|
-
leavesToSendToSsp = await this.
|
|
14160
|
-
|
|
14161
|
-
leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
|
|
14162
|
-
leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
|
|
13988
|
+
leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
|
|
13989
|
+
leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
|
|
14163
13990
|
const leafKeyTweaks = await Promise.all(
|
|
14164
13991
|
[...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
|
|
14165
13992
|
leaf,
|
|
@@ -14204,11 +14031,11 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14204
14031
|
const connectorOutputs = [];
|
|
14205
14032
|
for (let i = 0; i < connectorTx.outputsLength - 1; i++) {
|
|
14206
14033
|
connectorOutputs.push({
|
|
14207
|
-
txid:
|
|
14034
|
+
txid: hexToBytes12(connectorTxId),
|
|
14208
14035
|
index: i
|
|
14209
14036
|
});
|
|
14210
14037
|
}
|
|
14211
|
-
const sspPubIdentityKey =
|
|
14038
|
+
const sspPubIdentityKey = hexToBytes12(this.config.getSspIdentityPublicKey());
|
|
14212
14039
|
const transfer = await this.coopExitService.getConnectorRefundSignatures({
|
|
14213
14040
|
leaves: leafKeyTweaks,
|
|
14214
14041
|
exitTxId: coopExitTxId,
|
|
@@ -14245,8 +14072,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14245
14072
|
(await this.selectLeaves([amountSats])).get(amountSats),
|
|
14246
14073
|
`no leaves for ${amountSats}`
|
|
14247
14074
|
);
|
|
14248
|
-
leaves = await this.
|
|
14249
|
-
leaves = await this.checkExtendTimeLockNodes(leaves);
|
|
14075
|
+
leaves = await this.checkRenewLeaves(leaves);
|
|
14250
14076
|
const feeEstimate = await sspClient.getCoopExitFeeQuote({
|
|
14251
14077
|
leafExternalIds: leaves.map((leaf) => leaf.id),
|
|
14252
14078
|
withdrawalAddress
|
|
@@ -14521,7 +14347,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14521
14347
|
async validateMessageWithIdentityKey(message, signature) {
|
|
14522
14348
|
const hash = sha25613(message);
|
|
14523
14349
|
if (typeof signature === "string") {
|
|
14524
|
-
signature =
|
|
14350
|
+
signature = hexToBytes12(signature);
|
|
14525
14351
|
}
|
|
14526
14352
|
return this.config.signer.validateMessageWithIdentityKey(hash, signature);
|
|
14527
14353
|
}
|
|
@@ -14534,7 +14360,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14534
14360
|
*/
|
|
14535
14361
|
async signTransaction(txHex, keyType = "auto-detect") {
|
|
14536
14362
|
try {
|
|
14537
|
-
const tx =
|
|
14363
|
+
const tx = Transaction7.fromRaw(hexToBytes12(txHex));
|
|
14538
14364
|
let publicKey;
|
|
14539
14365
|
switch (keyType.toLowerCase()) {
|
|
14540
14366
|
case "identity":
|
|
@@ -14741,15 +14567,6 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14741
14567
|
}
|
|
14742
14568
|
);
|
|
14743
14569
|
}
|
|
14744
|
-
if (!nodeInput.sequence) {
|
|
14745
|
-
throw new ValidationError(
|
|
14746
|
-
`Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
|
|
14747
|
-
{
|
|
14748
|
-
field: "sequence",
|
|
14749
|
-
value: nodeInput.sequence
|
|
14750
|
-
}
|
|
14751
|
-
);
|
|
14752
|
-
}
|
|
14753
14570
|
const refundInput = refundTx.getInput(0);
|
|
14754
14571
|
if (!refundInput) {
|
|
14755
14572
|
throw new ValidationError(
|
|
@@ -14785,89 +14602,6 @@ var SparkWallet = class extends EventEmitter {
|
|
|
14785
14602
|
);
|
|
14786
14603
|
}
|
|
14787
14604
|
}
|
|
14788
|
-
/**
|
|
14789
|
-
* Refresh the timelock of a specific node.
|
|
14790
|
-
*
|
|
14791
|
-
* @param {string} nodeId - The ID of the node to refresh
|
|
14792
|
-
* @returns {Promise<void>} Promise that resolves when the timelock is refreshed
|
|
14793
|
-
*/
|
|
14794
|
-
async testOnly_expireTimelock(nodeId) {
|
|
14795
|
-
const sparkClient = await this.connectionManager.createSparkClient(
|
|
14796
|
-
this.config.getCoordinatorAddress()
|
|
14797
|
-
);
|
|
14798
|
-
try {
|
|
14799
|
-
const response = await sparkClient.query_nodes({
|
|
14800
|
-
source: {
|
|
14801
|
-
$case: "nodeIds",
|
|
14802
|
-
nodeIds: {
|
|
14803
|
-
nodeIds: [nodeId]
|
|
14804
|
-
}
|
|
14805
|
-
},
|
|
14806
|
-
includeParents: true
|
|
14807
|
-
});
|
|
14808
|
-
let leaf = response.nodes[nodeId];
|
|
14809
|
-
if (!leaf) {
|
|
14810
|
-
throw new ValidationError("Node not found", {
|
|
14811
|
-
field: "nodeId",
|
|
14812
|
-
value: nodeId
|
|
14813
|
-
});
|
|
14814
|
-
}
|
|
14815
|
-
let parentNode;
|
|
14816
|
-
let hasParentNode = false;
|
|
14817
|
-
if (!leaf.parentNodeId) {
|
|
14818
|
-
} else {
|
|
14819
|
-
hasParentNode = true;
|
|
14820
|
-
parentNode = response.nodes[leaf.parentNodeId];
|
|
14821
|
-
if (!parentNode) {
|
|
14822
|
-
throw new ValidationError("Parent node not found", {
|
|
14823
|
-
field: "parentNodeId",
|
|
14824
|
-
value: leaf.parentNodeId
|
|
14825
|
-
});
|
|
14826
|
-
}
|
|
14827
|
-
}
|
|
14828
|
-
const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
|
|
14829
|
-
const refundTx = getTxFromRawTxBytes(leaf.refundTx);
|
|
14830
|
-
if (hasParentNode) {
|
|
14831
|
-
const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
|
|
14832
|
-
if (nodeTimelock > 100) {
|
|
14833
|
-
const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
|
|
14834
|
-
leaf,
|
|
14835
|
-
parentNode
|
|
14836
|
-
);
|
|
14837
|
-
if (!expiredNodeTxLeaf.nodes[0]) {
|
|
14838
|
-
throw new ValidationError("No expired node tx leaf", {
|
|
14839
|
-
field: "expiredNodeTxLeaf",
|
|
14840
|
-
value: expiredNodeTxLeaf
|
|
14841
|
-
});
|
|
14842
|
-
}
|
|
14843
|
-
leaf = expiredNodeTxLeaf.nodes[0];
|
|
14844
|
-
}
|
|
14845
|
-
}
|
|
14846
|
-
const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
|
|
14847
|
-
if (refundTimelock > 100) {
|
|
14848
|
-
const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
|
|
14849
|
-
if (!expiredTxLeaf.nodes[0]) {
|
|
14850
|
-
throw new ValidationError("No expired tx leaf", {
|
|
14851
|
-
field: "expiredTxLeaf",
|
|
14852
|
-
value: expiredTxLeaf
|
|
14853
|
-
});
|
|
14854
|
-
}
|
|
14855
|
-
leaf = expiredTxLeaf.nodes[0];
|
|
14856
|
-
}
|
|
14857
|
-
const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
|
|
14858
|
-
if (leafIndex !== -1) {
|
|
14859
|
-
this.leaves[leafIndex] = leaf;
|
|
14860
|
-
}
|
|
14861
|
-
} catch (error) {
|
|
14862
|
-
throw new NetworkError(
|
|
14863
|
-
"Failed to refresh timelock",
|
|
14864
|
-
{
|
|
14865
|
-
method: "refresh_timelock"
|
|
14866
|
-
},
|
|
14867
|
-
error
|
|
14868
|
-
);
|
|
14869
|
-
}
|
|
14870
|
-
}
|
|
14871
14605
|
cleanup() {
|
|
14872
14606
|
if (this.claimTransfersInterval) {
|
|
14873
14607
|
clearInterval(this.claimTransfersInterval);
|
|
@@ -15022,8 +14756,7 @@ var SparkWallet = class extends EventEmitter {
|
|
|
15022
14756
|
"getLightningReceiveRequest",
|
|
15023
14757
|
"getLightningSendRequest",
|
|
15024
14758
|
"getCoopExitRequest",
|
|
15025
|
-
"checkTimelock"
|
|
15026
|
-
"testOnly_expireTimelock"
|
|
14759
|
+
"checkTimelock"
|
|
15027
14760
|
];
|
|
15028
14761
|
methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
|
|
15029
14762
|
this.initWallet = this.wrapWithOtelSpan(
|
|
@@ -15074,23 +14807,26 @@ export {
|
|
|
15074
14807
|
HTLC_TIMELOCK_OFFSET,
|
|
15075
14808
|
DIRECT_HTLC_TIMELOCK_OFFSET,
|
|
15076
14809
|
INITIAL_SEQUENCE,
|
|
15077
|
-
INITIAL_DIRECT_SEQUENCE,
|
|
15078
14810
|
TEST_UNILATERAL_SEQUENCE,
|
|
15079
14811
|
TEST_UNILATERAL_DIRECT_SEQUENCE,
|
|
15080
14812
|
DEFAULT_FEE_SATS,
|
|
15081
14813
|
maybeApplyFee,
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
createRefundTx,
|
|
14814
|
+
createRootNodeTx,
|
|
14815
|
+
createZeroTimelockNodeTx,
|
|
14816
|
+
createInitialTimelockNodeTx,
|
|
14817
|
+
createDecrementedTimelockNodeTx,
|
|
14818
|
+
createTestUnilateralTimelockNodeTx,
|
|
15088
14819
|
getNextHTLCTransactionSequence,
|
|
15089
|
-
|
|
15090
|
-
|
|
14820
|
+
createInitialTimelockRefundTxs,
|
|
14821
|
+
createDecrementedTimelockRefundTxs,
|
|
14822
|
+
createCurrentTimelockRefundTxs,
|
|
14823
|
+
createTestUnilateralRefundTxs,
|
|
14824
|
+
createConnectorRefundTxs,
|
|
15091
14825
|
getCurrentTimelock,
|
|
15092
14826
|
getTransactionSequence,
|
|
15093
14827
|
checkIfValidSequence,
|
|
14828
|
+
isZeroTimelock,
|
|
14829
|
+
doesTxnNeedRenewed,
|
|
15094
14830
|
doesLeafNeedRefresh,
|
|
15095
14831
|
getNextTransactionSequence,
|
|
15096
14832
|
getEphemeralAnchorOutput,
|
|
@@ -15164,6 +14900,7 @@ export {
|
|
|
15164
14900
|
isEphemeralAnchorOutput,
|
|
15165
14901
|
constructUnilateralExitTxs,
|
|
15166
14902
|
constructUnilateralExitFeeBumpPackages,
|
|
14903
|
+
hash160,
|
|
15167
14904
|
constructFeeBumpTx,
|
|
15168
14905
|
getSparkAddressFromTaproot,
|
|
15169
14906
|
utils_exports
|