@buildonspark/spark-sdk 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proto/spark.d.ts +111 -3
- package/dist/proto/spark.js +994 -43
- package/dist/proto/spark.js.map +1 -1
- package/dist/services/lightning.js +1 -0
- package/dist/services/lightning.js.map +1 -1
- package/dist/services/transfer.js +2 -0
- package/dist/services/transfer.js.map +1 -1
- package/dist/services/wallet-config.d.ts +1 -0
- package/dist/services/wallet-config.js +1 -0
- package/dist/services/wallet-config.js.map +1 -1
- package/dist/signer/signer.js +1 -1
- package/dist/signer/signer.js.map +1 -1
- package/dist/spark-sdk.d.ts +1 -1
- package/dist/spark-sdk.js +8 -4
- package/dist/spark-sdk.js.map +1 -1
- package/dist/tests/utils/test-faucet.d.ts +1 -0
- package/dist/tests/utils/test-faucet.js +9 -0
- package/dist/tests/utils/test-faucet.js.map +1 -1
- package/dist/utils/keys.d.ts +1 -1
- package/dist/utils/keys.js +4 -3
- package/dist/utils/keys.js.map +1 -1
- package/dist/utils/wasm-wrapper.js +9 -9
- package/dist/utils/wasm-wrapper.js.map +1 -1
- package/package.json +4 -3
- package/src/examples/example.js +247 -0
- package/src/examples/example.ts +207 -0
- package/src/graphql/client.ts +282 -0
- package/src/graphql/mutations/CompleteCoopExit.ts +19 -0
- package/src/graphql/mutations/CompleteLeavesSwap.ts +17 -0
- package/src/graphql/mutations/RequestCoopExit.ts +20 -0
- package/src/graphql/mutations/RequestLightningReceive.ts +26 -0
- package/src/graphql/mutations/RequestLightningSend.ts +17 -0
- package/src/graphql/mutations/RequestSwapLeaves.ts +24 -0
- package/src/graphql/objects/BitcoinNetwork.ts +22 -0
- package/src/graphql/objects/CompleteCoopExitInput.ts +41 -0
- package/src/graphql/objects/CompleteCoopExitOutput.ts +45 -0
- package/src/graphql/objects/CompleteLeavesSwapInput.ts +45 -0
- package/src/graphql/objects/CompleteLeavesSwapOutput.ts +45 -0
- package/src/graphql/objects/CompleteSeedReleaseInput.ts +41 -0
- package/src/graphql/objects/CompleteSeedReleaseOutput.ts +43 -0
- package/src/graphql/objects/Connection.ts +90 -0
- package/src/graphql/objects/CoopExitFeeEstimateInput.ts +41 -0
- package/src/graphql/objects/CoopExitFeeEstimateOutput.ts +52 -0
- package/src/graphql/objects/CoopExitRequest.ts +118 -0
- package/src/graphql/objects/CurrencyAmount.ts +74 -0
- package/src/graphql/objects/CurrencyUnit.ts +32 -0
- package/src/graphql/objects/Entity.ts +202 -0
- package/src/graphql/objects/GetChallengeInput.ts +37 -0
- package/src/graphql/objects/GetChallengeOutput.ts +43 -0
- package/src/graphql/objects/Invoice.ts +83 -0
- package/src/graphql/objects/Leaf.ts +59 -0
- package/src/graphql/objects/LeavesSwapFeeEstimateInput.ts +37 -0
- package/src/graphql/objects/LeavesSwapFeeEstimateOutput.ts +52 -0
- package/src/graphql/objects/LeavesSwapRequest.ts +192 -0
- package/src/graphql/objects/LightningReceiveFeeEstimateInput.ts +41 -0
- package/src/graphql/objects/LightningReceiveFeeEstimateOutput.ts +52 -0
- package/src/graphql/objects/LightningReceiveRequest.ts +147 -0
- package/src/graphql/objects/LightningReceiveRequestStatus.ts +34 -0
- package/src/graphql/objects/LightningSendFeeEstimateInput.ts +37 -0
- package/src/graphql/objects/LightningSendFeeEstimateOutput.ts +52 -0
- package/src/graphql/objects/LightningSendRequest.ts +134 -0
- package/src/graphql/objects/LightningSendRequestStatus.ts +28 -0
- package/src/graphql/objects/NotifyReceiverTransferInput.ts +41 -0
- package/src/graphql/objects/PageInfo.ts +58 -0
- package/src/graphql/objects/Provider.ts +41 -0
- package/src/graphql/objects/RequestCoopExitInput.ts +41 -0
- package/src/graphql/objects/RequestCoopExitOutput.ts +45 -0
- package/src/graphql/objects/RequestLeavesSwapInput.ts +55 -0
- package/src/graphql/objects/RequestLeavesSwapOutput.ts +45 -0
- package/src/graphql/objects/RequestLightningReceiveInput.ts +58 -0
- package/src/graphql/objects/RequestLightningReceiveOutput.ts +45 -0
- package/src/graphql/objects/RequestLightningSendInput.ts +41 -0
- package/src/graphql/objects/RequestLightningSendOutput.ts +45 -0
- package/src/graphql/objects/SparkCoopExitRequestStatus.ts +20 -0
- package/src/graphql/objects/SparkLeavesSwapRequestStatus.ts +20 -0
- package/src/graphql/objects/SparkTransferToLeavesConnection.ts +79 -0
- package/src/graphql/objects/SparkWalletUser.ts +86 -0
- package/src/graphql/objects/StartSeedReleaseInput.ts +37 -0
- package/src/graphql/objects/SwapLeaf.ts +53 -0
- package/src/graphql/objects/Transfer.ts +98 -0
- package/src/graphql/objects/UserLeafInput.ts +28 -0
- package/src/graphql/objects/VerifyChallengeInput.ts +51 -0
- package/src/graphql/objects/VerifyChallengeOutput.ts +43 -0
- package/src/graphql/objects/WalletUserIdentityPublicKeyInput.ts +37 -0
- package/src/graphql/objects/WalletUserIdentityPublicKeyOutput.ts +43 -0
- package/src/graphql/objects/index.ts +67 -0
- package/src/graphql/queries/CoopExitFeeEstimate.ts +18 -0
- package/src/graphql/queries/CurrentUser.ts +10 -0
- package/src/graphql/queries/LightningReceiveFeeEstimate.ts +18 -0
- package/src/graphql/queries/LightningSendFeeEstimate.ts +16 -0
- package/src/proto/common.ts +431 -0
- package/src/proto/google/protobuf/descriptor.ts +6625 -0
- package/src/proto/google/protobuf/duration.ts +197 -0
- package/src/proto/google/protobuf/empty.ts +83 -0
- package/src/proto/google/protobuf/timestamp.ts +226 -0
- package/src/proto/mock.ts +151 -0
- package/src/proto/spark.ts +12727 -0
- package/src/proto/spark_authn.ts +673 -0
- package/src/proto/validate/validate.ts +6047 -0
- package/src/services/config.ts +71 -0
- package/src/services/connection.ts +264 -0
- package/src/services/coop-exit.ts +190 -0
- package/src/services/deposit.ts +327 -0
- package/src/services/lightning.ts +341 -0
- package/src/services/lrc20.ts +42 -0
- package/src/services/token-transactions.ts +499 -0
- package/src/services/transfer.ts +1188 -0
- package/src/services/tree-creation.ts +618 -0
- package/src/services/wallet-config.ts +141 -0
- package/src/signer/signer.ts +531 -0
- package/src/spark-sdk.ts +1644 -0
- package/src/tests/adaptor-signature.test.ts +64 -0
- package/src/tests/bitcoin.test.ts +122 -0
- package/src/tests/coop-exit.test.ts +233 -0
- package/src/tests/deposit.test.ts +98 -0
- package/src/tests/keys.test.ts +82 -0
- package/src/tests/lightning.test.ts +307 -0
- package/src/tests/secret-sharing.test.ts +63 -0
- package/src/tests/swap.test.ts +252 -0
- package/src/tests/test-util.ts +92 -0
- package/src/tests/tokens.test.ts +47 -0
- package/src/tests/transfer.test.ts +371 -0
- package/src/tests/tree-creation.test.ts +56 -0
- package/src/tests/utils/spark-testing-wallet.ts +37 -0
- package/src/tests/utils/test-faucet.ts +257 -0
- package/src/types/grpc.ts +8 -0
- package/src/types/index.ts +3 -0
- package/src/utils/adaptor-signature.ts +189 -0
- package/src/utils/bitcoin.ts +138 -0
- package/src/utils/crypto.ts +14 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/keys.ts +92 -0
- package/src/utils/mempool.ts +42 -0
- package/src/utils/network.ts +70 -0
- package/src/utils/proof.ts +17 -0
- package/src/utils/response-validation.ts +26 -0
- package/src/utils/secret-sharing.ts +263 -0
- package/src/utils/signing.ts +96 -0
- package/src/utils/token-hashing.ts +163 -0
- package/src/utils/token-keyshares.ts +31 -0
- package/src/utils/token-transactions.ts +71 -0
- package/src/utils/transaction.ts +45 -0
- package/src/utils/wasm-wrapper.ts +57 -0
- package/src/utils/wasm.ts +154 -0
- package/src/wasm/spark_bindings.d.ts +208 -0
- package/src/wasm/spark_bindings.js +1161 -0
- package/src/wasm/spark_bindings_bg.wasm +0 -0
- package/src/wasm/spark_bindings_bg.wasm.d.ts +136 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { sha256 } from "@scure/btc-signer/utils";
|
|
2
|
+
import {
|
|
3
|
+
OperatorSpecificTokenTransactionSignablePayload,
|
|
4
|
+
TokenTransaction,
|
|
5
|
+
} from "../proto/spark.js";
|
|
6
|
+
|
|
7
|
+
export function hashTokenTransaction(
|
|
8
|
+
tokenTransaction: TokenTransaction,
|
|
9
|
+
partialHash: boolean = false,
|
|
10
|
+
): Uint8Array {
|
|
11
|
+
if (!tokenTransaction) {
|
|
12
|
+
throw new Error("token transaction cannot be nil");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let allHashes: Uint8Array[] = [];
|
|
16
|
+
|
|
17
|
+
// Hash input leaves if a transfer
|
|
18
|
+
if (tokenTransaction.tokenInput?.$case === "transferInput") {
|
|
19
|
+
// Hash leaves_to_spend
|
|
20
|
+
for (const leaf of tokenTransaction.tokenInput!.transferInput!
|
|
21
|
+
.leavesToSpend || []) {
|
|
22
|
+
const hashObj = sha256.create();
|
|
23
|
+
|
|
24
|
+
if (leaf.prevTokenTransactionHash) {
|
|
25
|
+
hashObj.update(leaf.prevTokenTransactionHash);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const voutBytes = new Uint8Array(4);
|
|
29
|
+
new DataView(voutBytes.buffer).setUint32(
|
|
30
|
+
0,
|
|
31
|
+
leaf.prevTokenTransactionLeafVout,
|
|
32
|
+
false,
|
|
33
|
+
); // false for big-endian
|
|
34
|
+
hashObj.update(voutBytes);
|
|
35
|
+
|
|
36
|
+
allHashes.push(hashObj.digest());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Hash input issuance if an issuance
|
|
41
|
+
if (tokenTransaction.tokenInput?.$case === "mintInput") {
|
|
42
|
+
const hashObj = sha256.create();
|
|
43
|
+
if (tokenTransaction.tokenInput.mintInput!.issuerPublicKey) {
|
|
44
|
+
hashObj.update(tokenTransaction.tokenInput.mintInput!.issuerPublicKey);
|
|
45
|
+
}
|
|
46
|
+
if (tokenTransaction.tokenInput.mintInput!.issuerProvidedTimestamp) {
|
|
47
|
+
const timestampBytes = new Uint8Array(8);
|
|
48
|
+
new DataView(timestampBytes.buffer).setBigUint64(
|
|
49
|
+
0,
|
|
50
|
+
BigInt(tokenTransaction.tokenInput.mintInput!.issuerProvidedTimestamp),
|
|
51
|
+
true, // true for little-endian to match Go implementation
|
|
52
|
+
);
|
|
53
|
+
hashObj.update(timestampBytes);
|
|
54
|
+
}
|
|
55
|
+
allHashes.push(hashObj.digest());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Hash output leaves
|
|
59
|
+
for (const leaf of tokenTransaction.outputLeaves || []) {
|
|
60
|
+
const hashObj = sha256.create();
|
|
61
|
+
|
|
62
|
+
// Only hash ID if it's not empty and not in partial hash mode
|
|
63
|
+
if (leaf.id && !partialHash) {
|
|
64
|
+
hashObj.update(new TextEncoder().encode(leaf.id));
|
|
65
|
+
}
|
|
66
|
+
if (leaf.ownerPublicKey) {
|
|
67
|
+
hashObj.update(leaf.ownerPublicKey);
|
|
68
|
+
}
|
|
69
|
+
if (leaf.revocationPublicKey && !partialHash) {
|
|
70
|
+
hashObj.update(leaf.revocationPublicKey);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (leaf.withdrawBondSats && !partialHash) {
|
|
74
|
+
const bondBytes = new Uint8Array(8);
|
|
75
|
+
new DataView(bondBytes.buffer).setBigUint64(
|
|
76
|
+
0,
|
|
77
|
+
BigInt(leaf.withdrawBondSats!),
|
|
78
|
+
false,
|
|
79
|
+
);
|
|
80
|
+
hashObj.update(bondBytes);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (leaf.withdrawRelativeBlockLocktime && !partialHash) {
|
|
84
|
+
const locktimeBytes = new Uint8Array(8);
|
|
85
|
+
new DataView(locktimeBytes.buffer).setBigUint64(
|
|
86
|
+
0,
|
|
87
|
+
BigInt(leaf.withdrawRelativeBlockLocktime!),
|
|
88
|
+
false,
|
|
89
|
+
);
|
|
90
|
+
hashObj.update(locktimeBytes);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (leaf.tokenPublicKey) {
|
|
94
|
+
hashObj.update(leaf.tokenPublicKey);
|
|
95
|
+
}
|
|
96
|
+
if (leaf.tokenAmount) {
|
|
97
|
+
hashObj.update(leaf.tokenAmount);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
allHashes.push(hashObj.digest());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Sort operator public keys before hashing
|
|
104
|
+
const sortedPubKeys = [
|
|
105
|
+
...(tokenTransaction.sparkOperatorIdentityPublicKeys || []),
|
|
106
|
+
].sort((a, b) => {
|
|
107
|
+
for (let i = 0; i < a.length && i < b.length; i++) {
|
|
108
|
+
// @ts-ignore - i < a and b length
|
|
109
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
110
|
+
}
|
|
111
|
+
return a.length - b.length;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Hash spark operator identity public keys
|
|
115
|
+
for (const pubKey of sortedPubKeys) {
|
|
116
|
+
const hashObj = sha256.create();
|
|
117
|
+
if (pubKey) {
|
|
118
|
+
hashObj.update(pubKey);
|
|
119
|
+
}
|
|
120
|
+
allHashes.push(hashObj.digest());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Final hash of all concatenated hashes
|
|
124
|
+
const finalHashObj = sha256.create();
|
|
125
|
+
const concatenatedHashes = new Uint8Array(
|
|
126
|
+
allHashes.reduce((sum, hash) => sum + hash.length, 0),
|
|
127
|
+
);
|
|
128
|
+
let offset = 0;
|
|
129
|
+
for (const hash of allHashes) {
|
|
130
|
+
concatenatedHashes.set(hash, offset);
|
|
131
|
+
offset += hash.length;
|
|
132
|
+
}
|
|
133
|
+
finalHashObj.update(concatenatedHashes);
|
|
134
|
+
return finalHashObj.digest();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function hashOperatorSpecificTokenTransactionSignablePayload(
|
|
138
|
+
payload: OperatorSpecificTokenTransactionSignablePayload,
|
|
139
|
+
): Uint8Array {
|
|
140
|
+
if (!payload) {
|
|
141
|
+
throw new Error("revocation keyshare signable payload cannot be nil");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let allHashes = new Uint8Array(0);
|
|
145
|
+
|
|
146
|
+
// Hash final_token_transaction_hash
|
|
147
|
+
const hash1 = sha256(payload.finalTokenTransactionHash || new Uint8Array(0));
|
|
148
|
+
allHashes = concatenateUint8Arrays(allHashes, hash1);
|
|
149
|
+
|
|
150
|
+
// Hash operator_identity_public_key
|
|
151
|
+
const hash2 = sha256(payload.operatorIdentityPublicKey || new Uint8Array(0));
|
|
152
|
+
allHashes = concatenateUint8Arrays(allHashes, hash2);
|
|
153
|
+
|
|
154
|
+
// Final hash of all concatenated hashes
|
|
155
|
+
return sha256(allHashes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function concatenateUint8Arrays(array1: Uint8Array, array2: Uint8Array) {
|
|
159
|
+
const result = new Uint8Array(array1.length + array2.length);
|
|
160
|
+
result.set(array1, 0);
|
|
161
|
+
result.set(array2, array1.length);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
2
|
+
import {
|
|
3
|
+
bigIntToPrivateKey,
|
|
4
|
+
recoverSecret,
|
|
5
|
+
VerifiableSecretShare,
|
|
6
|
+
} from "./secret-sharing.js";
|
|
7
|
+
|
|
8
|
+
export interface KeyshareWithOperatorIndex {
|
|
9
|
+
index: number;
|
|
10
|
+
keyshare: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function recoverPrivateKeyFromKeyshares(
|
|
14
|
+
keyshares: KeyshareWithOperatorIndex[],
|
|
15
|
+
threshold: number,
|
|
16
|
+
): Uint8Array {
|
|
17
|
+
// Convert keyshares to secret shares format
|
|
18
|
+
const shares: VerifiableSecretShare[] = keyshares.map((keyshare) => ({
|
|
19
|
+
fieldModulus: BigInt("0x" + secp256k1.CURVE.n.toString(16)), // secp256k1 curve order
|
|
20
|
+
threshold,
|
|
21
|
+
index: BigInt(keyshare.index),
|
|
22
|
+
share: BigInt("0x" + Buffer.from(keyshare.keyshare).toString("hex")),
|
|
23
|
+
proofs: [],
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// Recover the secret
|
|
27
|
+
const recoveredKey = recoverSecret(shares);
|
|
28
|
+
|
|
29
|
+
// Convert to bytes
|
|
30
|
+
return bigIntToPrivateKey(recoveredKey);
|
|
31
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { bytesToHex, bytesToNumberBE } from "@noble/curves/abstract/utils";
|
|
2
|
+
import {
|
|
3
|
+
LeafWithPreviousTransactionData,
|
|
4
|
+
TokenTransaction,
|
|
5
|
+
} from "../proto/spark.js";
|
|
6
|
+
import { hashTokenTransaction } from "./token-hashing.js";
|
|
7
|
+
|
|
8
|
+
export function getTokenLeavesSum(
|
|
9
|
+
leaves: LeafWithPreviousTransactionData[],
|
|
10
|
+
): bigint {
|
|
11
|
+
return leaves.reduce(
|
|
12
|
+
(sum, leaf) => sum + BigInt(bytesToNumberBE(leaf.leaf!.tokenAmount!)),
|
|
13
|
+
BigInt(0),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function extractOutputLeaves(
|
|
18
|
+
fullTokenTransaction: TokenTransaction,
|
|
19
|
+
): LeafWithPreviousTransactionData[] {
|
|
20
|
+
const outputLeaves: LeafWithPreviousTransactionData[] = [];
|
|
21
|
+
const hash = hashTokenTransaction(fullTokenTransaction, true);
|
|
22
|
+
|
|
23
|
+
fullTokenTransaction.outputLeaves!.forEach((output, index) => {
|
|
24
|
+
outputLeaves.push({
|
|
25
|
+
leaf: output,
|
|
26
|
+
previousTransactionHash: hash!,
|
|
27
|
+
previousTransactionVout: index,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
return outputLeaves;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function calculateAvailableTokenAmount(
|
|
34
|
+
outputLeaves: LeafWithPreviousTransactionData[],
|
|
35
|
+
): bigint {
|
|
36
|
+
return outputLeaves.reduce(
|
|
37
|
+
(sum, leaf) => sum + BigInt(bytesToNumberBE(leaf.leaf!.tokenAmount!)),
|
|
38
|
+
BigInt(0),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function checkIfSelectedLeavesAreAvailable(
|
|
43
|
+
selectedLeaves: LeafWithPreviousTransactionData[],
|
|
44
|
+
tokenLeaves: Map<string, LeafWithPreviousTransactionData[]>,
|
|
45
|
+
tokenPublicKey: Uint8Array,
|
|
46
|
+
) {
|
|
47
|
+
const tokenPubKeyHex = bytesToHex(tokenPublicKey);
|
|
48
|
+
const tokenLeavesAvailable = tokenLeaves.get(tokenPubKeyHex);
|
|
49
|
+
if (!tokenLeavesAvailable) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (
|
|
53
|
+
selectedLeaves.length === 0 ||
|
|
54
|
+
tokenLeavesAvailable.length < selectedLeaves.length
|
|
55
|
+
) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Create a Set of available leaf IDs for O(n + m) lookup
|
|
60
|
+
const availableLeafIds = new Set(
|
|
61
|
+
tokenLeavesAvailable.map((leaf) => leaf.leaf!.id),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
for (const selectedLeaf of selectedLeaves) {
|
|
65
|
+
if (!selectedLeaf.leaf?.id || !availableLeafIds.has(selectedLeaf.leaf.id)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Transaction } from "@scure/btc-signer";
|
|
2
|
+
import { TransactionInput, TransactionOutput } from "@scure/btc-signer/psbt";
|
|
3
|
+
import { getP2TRScriptFromPublicKey } from "./bitcoin.js";
|
|
4
|
+
import { Network } from "./network.js";
|
|
5
|
+
|
|
6
|
+
const TIME_LOCK_INTERVAL = 100;
|
|
7
|
+
|
|
8
|
+
export function createRefundTx(
|
|
9
|
+
sequence: number,
|
|
10
|
+
nodeOutPoint: TransactionInput,
|
|
11
|
+
amountSats: bigint,
|
|
12
|
+
receivingPubkey: Uint8Array,
|
|
13
|
+
network: Network,
|
|
14
|
+
): Transaction {
|
|
15
|
+
const newRefundTx = new Transaction({ allowUnknownOutputs: true });
|
|
16
|
+
newRefundTx.addInput({
|
|
17
|
+
...nodeOutPoint,
|
|
18
|
+
sequence,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
|
|
22
|
+
|
|
23
|
+
newRefundTx.addOutput({
|
|
24
|
+
script: refundPkScript,
|
|
25
|
+
amount: amountSats,
|
|
26
|
+
});
|
|
27
|
+
newRefundTx.addOutput(getEphemeralAnchorOutput());
|
|
28
|
+
|
|
29
|
+
return newRefundTx;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getNextTransactionSequence(currSequence?: number): number {
|
|
33
|
+
const currentTimelock = (currSequence || 0) & 0xffff;
|
|
34
|
+
if (currentTimelock - TIME_LOCK_INTERVAL <= 0) {
|
|
35
|
+
throw new Error("timelock interval is less or equal to 0");
|
|
36
|
+
}
|
|
37
|
+
return (1 << 30) | (currentTimelock - TIME_LOCK_INTERVAL);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getEphemeralAnchorOutput(): TransactionOutput {
|
|
41
|
+
return {
|
|
42
|
+
script: new Uint8Array([0x51]), // OP_TRUE
|
|
43
|
+
amount: 0n,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import init, { InitOutput } from "../wasm/spark_bindings.js";
|
|
2
|
+
|
|
3
|
+
export async function initWasm(): Promise<InitOutput> {
|
|
4
|
+
let wasmModule: InitOutput;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
if (typeof window === "undefined") {
|
|
8
|
+
// Node.js environment
|
|
9
|
+
try {
|
|
10
|
+
// Dynamic imports for Node.js modules to avoid browser compatibility issues
|
|
11
|
+
const fs = await import("fs/promises");
|
|
12
|
+
const path = await import("path");
|
|
13
|
+
const url = await import("url");
|
|
14
|
+
|
|
15
|
+
const __filename = url.fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
const wasmPath = path.resolve(
|
|
19
|
+
__dirname,
|
|
20
|
+
"../wasm/spark_bindings_bg.wasm",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const wasmBuffer = await fs.readFile(wasmPath);
|
|
24
|
+
|
|
25
|
+
// Initialize with proper memory configuration for Node.js
|
|
26
|
+
wasmModule = await init({ module_or_path: wasmBuffer }).catch((e) => {
|
|
27
|
+
console.error("WASM initialization error:", e);
|
|
28
|
+
throw e;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return wasmModule;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error(
|
|
34
|
+
"Error with Node.js-specific WASM loading, falling back to standard initialization:",
|
|
35
|
+
e,
|
|
36
|
+
);
|
|
37
|
+
// Fall back to standard initialization if dynamic imports fail
|
|
38
|
+
wasmModule = await init();
|
|
39
|
+
return wasmModule;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
// Browser environment
|
|
43
|
+
const response = await fetch(
|
|
44
|
+
new URL("../wasm/spark_bindings_bg.wasm", import.meta.url),
|
|
45
|
+
);
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new Error(`Failed to load WASM file: ${response.statusText}`);
|
|
48
|
+
}
|
|
49
|
+
const wasmBuffer = await response.arrayBuffer();
|
|
50
|
+
wasmModule = await init({ module_or_path: wasmBuffer });
|
|
51
|
+
return wasmModule;
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("WASM initialization error:", e);
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
construct_node_tx,
|
|
3
|
+
construct_refund_tx,
|
|
4
|
+
construct_split_tx,
|
|
5
|
+
create_dummy_tx,
|
|
6
|
+
decrypt_ecies,
|
|
7
|
+
DummyTx,
|
|
8
|
+
encrypt_ecies,
|
|
9
|
+
KeyPackage,
|
|
10
|
+
SigningCommitment,
|
|
11
|
+
SigningNonce,
|
|
12
|
+
TransactionResult,
|
|
13
|
+
wasm_aggregate_frost,
|
|
14
|
+
wasm_sign_frost,
|
|
15
|
+
} from "../wasm/spark_bindings.js";
|
|
16
|
+
|
|
17
|
+
export type SignFrostParams = {
|
|
18
|
+
msg: Uint8Array;
|
|
19
|
+
keyPackage: KeyPackage;
|
|
20
|
+
nonce: SigningNonce;
|
|
21
|
+
selfCommitment: SigningCommitment;
|
|
22
|
+
statechainCommitments: any;
|
|
23
|
+
adaptorPubKey?: Uint8Array | undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type AggregateFrostParams = {
|
|
27
|
+
msg: Uint8Array;
|
|
28
|
+
statechainCommitments: any;
|
|
29
|
+
selfCommitment: SigningCommitment;
|
|
30
|
+
statechainSignatures: any;
|
|
31
|
+
selfSignature: Uint8Array;
|
|
32
|
+
statechainPublicKeys: any;
|
|
33
|
+
selfPublicKey: Uint8Array;
|
|
34
|
+
verifyingKey: Uint8Array;
|
|
35
|
+
adaptorPubKey?: Uint8Array | undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type ConstructNodeTxParams = {
|
|
39
|
+
tx: Uint8Array;
|
|
40
|
+
vout: number;
|
|
41
|
+
address: string;
|
|
42
|
+
locktime: number;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function signFrost({
|
|
46
|
+
msg,
|
|
47
|
+
keyPackage,
|
|
48
|
+
nonce,
|
|
49
|
+
selfCommitment,
|
|
50
|
+
statechainCommitments,
|
|
51
|
+
adaptorPubKey,
|
|
52
|
+
}: SignFrostParams): Uint8Array {
|
|
53
|
+
return wasm_sign_frost(
|
|
54
|
+
msg,
|
|
55
|
+
keyPackage,
|
|
56
|
+
nonce,
|
|
57
|
+
selfCommitment,
|
|
58
|
+
statechainCommitments,
|
|
59
|
+
adaptorPubKey,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function aggregateFrost({
|
|
64
|
+
msg,
|
|
65
|
+
statechainCommitments,
|
|
66
|
+
selfCommitment,
|
|
67
|
+
statechainSignatures,
|
|
68
|
+
selfSignature,
|
|
69
|
+
statechainPublicKeys,
|
|
70
|
+
selfPublicKey,
|
|
71
|
+
verifyingKey,
|
|
72
|
+
adaptorPubKey,
|
|
73
|
+
}: AggregateFrostParams): Uint8Array {
|
|
74
|
+
return wasm_aggregate_frost(
|
|
75
|
+
msg,
|
|
76
|
+
statechainCommitments,
|
|
77
|
+
selfCommitment,
|
|
78
|
+
statechainSignatures,
|
|
79
|
+
selfSignature,
|
|
80
|
+
statechainPublicKeys,
|
|
81
|
+
selfPublicKey,
|
|
82
|
+
verifyingKey,
|
|
83
|
+
adaptorPubKey,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function constructNodeTx({
|
|
88
|
+
tx,
|
|
89
|
+
vout,
|
|
90
|
+
address,
|
|
91
|
+
locktime,
|
|
92
|
+
}: ConstructNodeTxParams): TransactionResult {
|
|
93
|
+
return construct_node_tx(tx, vout, address, locktime);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function constructRefundTx({
|
|
97
|
+
tx,
|
|
98
|
+
vout,
|
|
99
|
+
pubkey,
|
|
100
|
+
network,
|
|
101
|
+
locktime,
|
|
102
|
+
}: {
|
|
103
|
+
tx: Uint8Array;
|
|
104
|
+
vout: number;
|
|
105
|
+
pubkey: Uint8Array;
|
|
106
|
+
network: string;
|
|
107
|
+
locktime: number;
|
|
108
|
+
}): TransactionResult {
|
|
109
|
+
return construct_refund_tx(tx, vout, pubkey, network, locktime);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function constructSplitTx({
|
|
113
|
+
tx,
|
|
114
|
+
vout,
|
|
115
|
+
addresses,
|
|
116
|
+
locktime,
|
|
117
|
+
}: {
|
|
118
|
+
tx: Uint8Array;
|
|
119
|
+
vout: number;
|
|
120
|
+
addresses: string[];
|
|
121
|
+
locktime: number;
|
|
122
|
+
}): TransactionResult {
|
|
123
|
+
return construct_split_tx(tx, vout, addresses, locktime);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createDummyTx({
|
|
127
|
+
address,
|
|
128
|
+
amountSats,
|
|
129
|
+
}: {
|
|
130
|
+
address: string;
|
|
131
|
+
amountSats: bigint;
|
|
132
|
+
}): DummyTx {
|
|
133
|
+
return create_dummy_tx(address, amountSats);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function encryptEcies({
|
|
137
|
+
msg,
|
|
138
|
+
publicKey,
|
|
139
|
+
}: {
|
|
140
|
+
msg: Uint8Array;
|
|
141
|
+
publicKey: Uint8Array;
|
|
142
|
+
}): Uint8Array {
|
|
143
|
+
return encrypt_ecies(msg, publicKey);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function decryptEcies({
|
|
147
|
+
encryptedMsg,
|
|
148
|
+
privateKey,
|
|
149
|
+
}: {
|
|
150
|
+
encryptedMsg: Uint8Array;
|
|
151
|
+
privateKey: Uint8Array;
|
|
152
|
+
}): Uint8Array {
|
|
153
|
+
return decrypt_ecies(encryptedMsg, privateKey);
|
|
154
|
+
}
|