@buildonspark/spark-sdk 0.0.15 → 0.0.17

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.
Files changed (136) hide show
  1. package/dist/services/config.d.ts +1 -0
  2. package/dist/services/config.js +4 -1
  3. package/dist/services/config.js.map +1 -1
  4. package/dist/services/wallet-config.d.ts +2 -0
  5. package/dist/services/wallet-config.js +2 -0
  6. package/dist/services/wallet-config.js.map +1 -1
  7. package/dist/signer/signer.js +4 -3
  8. package/dist/signer/signer.js.map +1 -1
  9. package/dist/spark-sdk.d.ts +4 -4
  10. package/dist/spark-sdk.js +26 -10
  11. package/dist/spark-sdk.js.map +1 -1
  12. package/package.json +4 -3
  13. package/src/examples/example.js +247 -0
  14. package/src/examples/example.ts +207 -0
  15. package/src/graphql/client.ts +282 -0
  16. package/src/graphql/mutations/CompleteCoopExit.ts +19 -0
  17. package/src/graphql/mutations/CompleteLeavesSwap.ts +17 -0
  18. package/src/graphql/mutations/RequestCoopExit.ts +20 -0
  19. package/src/graphql/mutations/RequestLightningReceive.ts +26 -0
  20. package/src/graphql/mutations/RequestLightningSend.ts +17 -0
  21. package/src/graphql/mutations/RequestSwapLeaves.ts +24 -0
  22. package/src/graphql/objects/BitcoinNetwork.ts +22 -0
  23. package/src/graphql/objects/CompleteCoopExitInput.ts +41 -0
  24. package/src/graphql/objects/CompleteCoopExitOutput.ts +45 -0
  25. package/src/graphql/objects/CompleteLeavesSwapInput.ts +45 -0
  26. package/src/graphql/objects/CompleteLeavesSwapOutput.ts +45 -0
  27. package/src/graphql/objects/CompleteSeedReleaseInput.ts +41 -0
  28. package/src/graphql/objects/CompleteSeedReleaseOutput.ts +43 -0
  29. package/src/graphql/objects/Connection.ts +90 -0
  30. package/src/graphql/objects/CoopExitFeeEstimateInput.ts +41 -0
  31. package/src/graphql/objects/CoopExitFeeEstimateOutput.ts +52 -0
  32. package/src/graphql/objects/CoopExitRequest.ts +118 -0
  33. package/src/graphql/objects/CurrencyAmount.ts +74 -0
  34. package/src/graphql/objects/CurrencyUnit.ts +32 -0
  35. package/src/graphql/objects/Entity.ts +202 -0
  36. package/src/graphql/objects/GetChallengeInput.ts +37 -0
  37. package/src/graphql/objects/GetChallengeOutput.ts +43 -0
  38. package/src/graphql/objects/Invoice.ts +83 -0
  39. package/src/graphql/objects/Leaf.ts +59 -0
  40. package/src/graphql/objects/LeavesSwapFeeEstimateInput.ts +37 -0
  41. package/src/graphql/objects/LeavesSwapFeeEstimateOutput.ts +52 -0
  42. package/src/graphql/objects/LeavesSwapRequest.ts +192 -0
  43. package/src/graphql/objects/LightningReceiveFeeEstimateInput.ts +41 -0
  44. package/src/graphql/objects/LightningReceiveFeeEstimateOutput.ts +52 -0
  45. package/src/graphql/objects/LightningReceiveRequest.ts +147 -0
  46. package/src/graphql/objects/LightningReceiveRequestStatus.ts +34 -0
  47. package/src/graphql/objects/LightningSendFeeEstimateInput.ts +37 -0
  48. package/src/graphql/objects/LightningSendFeeEstimateOutput.ts +52 -0
  49. package/src/graphql/objects/LightningSendRequest.ts +134 -0
  50. package/src/graphql/objects/LightningSendRequestStatus.ts +28 -0
  51. package/src/graphql/objects/NotifyReceiverTransferInput.ts +41 -0
  52. package/src/graphql/objects/PageInfo.ts +58 -0
  53. package/src/graphql/objects/Provider.ts +41 -0
  54. package/src/graphql/objects/RequestCoopExitInput.ts +41 -0
  55. package/src/graphql/objects/RequestCoopExitOutput.ts +45 -0
  56. package/src/graphql/objects/RequestLeavesSwapInput.ts +55 -0
  57. package/src/graphql/objects/RequestLeavesSwapOutput.ts +45 -0
  58. package/src/graphql/objects/RequestLightningReceiveInput.ts +58 -0
  59. package/src/graphql/objects/RequestLightningReceiveOutput.ts +45 -0
  60. package/src/graphql/objects/RequestLightningSendInput.ts +41 -0
  61. package/src/graphql/objects/RequestLightningSendOutput.ts +45 -0
  62. package/src/graphql/objects/SparkCoopExitRequestStatus.ts +20 -0
  63. package/src/graphql/objects/SparkLeavesSwapRequestStatus.ts +20 -0
  64. package/src/graphql/objects/SparkTransferToLeavesConnection.ts +79 -0
  65. package/src/graphql/objects/SparkWalletUser.ts +86 -0
  66. package/src/graphql/objects/StartSeedReleaseInput.ts +37 -0
  67. package/src/graphql/objects/SwapLeaf.ts +53 -0
  68. package/src/graphql/objects/Transfer.ts +98 -0
  69. package/src/graphql/objects/UserLeafInput.ts +28 -0
  70. package/src/graphql/objects/VerifyChallengeInput.ts +51 -0
  71. package/src/graphql/objects/VerifyChallengeOutput.ts +43 -0
  72. package/src/graphql/objects/WalletUserIdentityPublicKeyInput.ts +37 -0
  73. package/src/graphql/objects/WalletUserIdentityPublicKeyOutput.ts +43 -0
  74. package/src/graphql/objects/index.ts +67 -0
  75. package/src/graphql/queries/CoopExitFeeEstimate.ts +18 -0
  76. package/src/graphql/queries/CurrentUser.ts +10 -0
  77. package/src/graphql/queries/LightningReceiveFeeEstimate.ts +18 -0
  78. package/src/graphql/queries/LightningSendFeeEstimate.ts +16 -0
  79. package/src/proto/common.ts +431 -0
  80. package/src/proto/google/protobuf/descriptor.ts +6625 -0
  81. package/src/proto/google/protobuf/duration.ts +197 -0
  82. package/src/proto/google/protobuf/empty.ts +83 -0
  83. package/src/proto/google/protobuf/timestamp.ts +226 -0
  84. package/src/proto/mock.ts +151 -0
  85. package/src/proto/spark.ts +12727 -0
  86. package/src/proto/spark_authn.ts +673 -0
  87. package/src/proto/validate/validate.ts +6047 -0
  88. package/src/services/config.ts +75 -0
  89. package/src/services/connection.ts +264 -0
  90. package/src/services/coop-exit.ts +190 -0
  91. package/src/services/deposit.ts +327 -0
  92. package/src/services/lightning.ts +341 -0
  93. package/src/services/lrc20.ts +42 -0
  94. package/src/services/token-transactions.ts +499 -0
  95. package/src/services/transfer.ts +1188 -0
  96. package/src/services/tree-creation.ts +618 -0
  97. package/src/services/wallet-config.ts +143 -0
  98. package/src/signer/signer.ts +532 -0
  99. package/src/spark-sdk.ts +1665 -0
  100. package/src/tests/adaptor-signature.test.ts +64 -0
  101. package/src/tests/bitcoin.test.ts +122 -0
  102. package/src/tests/coop-exit.test.ts +233 -0
  103. package/src/tests/deposit.test.ts +98 -0
  104. package/src/tests/keys.test.ts +82 -0
  105. package/src/tests/lightning.test.ts +307 -0
  106. package/src/tests/secret-sharing.test.ts +63 -0
  107. package/src/tests/swap.test.ts +252 -0
  108. package/src/tests/test-util.ts +92 -0
  109. package/src/tests/tokens.test.ts +47 -0
  110. package/src/tests/transfer.test.ts +371 -0
  111. package/src/tests/tree-creation.test.ts +56 -0
  112. package/src/tests/utils/spark-testing-wallet.ts +37 -0
  113. package/src/tests/utils/test-faucet.ts +257 -0
  114. package/src/types/grpc.ts +8 -0
  115. package/src/types/index.ts +3 -0
  116. package/src/utils/adaptor-signature.ts +189 -0
  117. package/src/utils/bitcoin.ts +138 -0
  118. package/src/utils/crypto.ts +14 -0
  119. package/src/utils/index.ts +12 -0
  120. package/src/utils/keys.ts +92 -0
  121. package/src/utils/mempool.ts +42 -0
  122. package/src/utils/network.ts +70 -0
  123. package/src/utils/proof.ts +17 -0
  124. package/src/utils/response-validation.ts +26 -0
  125. package/src/utils/secret-sharing.ts +263 -0
  126. package/src/utils/signing.ts +96 -0
  127. package/src/utils/token-hashing.ts +163 -0
  128. package/src/utils/token-keyshares.ts +31 -0
  129. package/src/utils/token-transactions.ts +71 -0
  130. package/src/utils/transaction.ts +45 -0
  131. package/src/utils/wasm-wrapper.ts +57 -0
  132. package/src/utils/wasm.ts +154 -0
  133. package/src/wasm/spark_bindings.d.ts +208 -0
  134. package/src/wasm/spark_bindings.js +1161 -0
  135. package/src/wasm/spark_bindings_bg.wasm +0 -0
  136. package/src/wasm/spark_bindings_bg.wasm.d.ts +136 -0
@@ -0,0 +1,143 @@
1
+ import { hexToBytes } from "@noble/curves/abstract/utils";
2
+ import { NetworkType } from "../utils/network.js";
3
+
4
+ export type SigningOperator = {
5
+ readonly id: number;
6
+ readonly identifier: string;
7
+ readonly address: string;
8
+ readonly identityPublicKey: Uint8Array;
9
+ };
10
+
11
+ export type ConfigOptions = {
12
+ readonly network?: NetworkType;
13
+ readonly signingOperators?: Readonly<Record<string, SigningOperator>>;
14
+ readonly coodinatorIdentifier?: string;
15
+ readonly frostSignerAddress?: string;
16
+ readonly lrc20Address?: string;
17
+ readonly threshold?: number;
18
+ readonly useTokenTransactionSchnorrSignatures?: boolean;
19
+ readonly enableLrc20?: boolean;
20
+ };
21
+
22
+ const BASE_CONFIG: Required<ConfigOptions> = {
23
+ network: "LOCAL",
24
+ lrc20Address: "http://127.0.0.1:18530",
25
+ coodinatorIdentifier:
26
+ "0000000000000000000000000000000000000000000000000000000000000001",
27
+ frostSignerAddress: "unix:///tmp/frost_0.sock",
28
+ threshold: 3,
29
+ signingOperators: getLocalSigningOperators(),
30
+ useTokenTransactionSchnorrSignatures: true,
31
+ enableLrc20: true,
32
+ };
33
+
34
+ export const LOCAL_WALLET_CONFIG: Required<ConfigOptions> = {
35
+ ...BASE_CONFIG,
36
+ };
37
+
38
+ export const LOCAL_WALLET_CONFIG_SCHNORR: Required<ConfigOptions> = {
39
+ ...BASE_CONFIG,
40
+ };
41
+
42
+ export const LOCAL_WALLET_CONFIG_ECDSA: Required<ConfigOptions> = {
43
+ ...BASE_CONFIG,
44
+ useTokenTransactionSchnorrSignatures: false,
45
+ };
46
+
47
+ export const REGTEST_WALLET_CONFIG: Required<ConfigOptions> = {
48
+ ...BASE_CONFIG,
49
+ network: "REGTEST",
50
+ signingOperators: getRegtestSigningOperators(),
51
+ };
52
+
53
+ export const MAINNET_WALLET_CONFIG: Required<ConfigOptions> = {
54
+ ...BASE_CONFIG,
55
+ network: "MAINNET",
56
+ signingOperators: getRegtestSigningOperators(),
57
+ };
58
+
59
+ export function getRegtestSigningOperators(): Record<string, SigningOperator> {
60
+ const pubkeys = [
61
+ "03acd9a5a88db102730ff83dee69d69088cc4c9d93bbee893e90fd5051b7da9651",
62
+ "02d2d103cacb1d6355efeab27637c74484e2a7459e49110c3fe885210369782e23",
63
+ "0350f07ffc21bfd59d31e0a7a600e2995273938444447cb9bc4c75b8a895dbb853",
64
+ ];
65
+
66
+ const pubkeyBytesArray = pubkeys.map((pubkey) => hexToBytes(pubkey));
67
+
68
+ return {
69
+ "0000000000000000000000000000000000000000000000000000000000000001": {
70
+ id: 0,
71
+ identifier:
72
+ "0000000000000000000000000000000000000000000000000000000000000001",
73
+ address: "https://0.spark.dev.dev.sparkinfra.net",
74
+
75
+ identityPublicKey: pubkeyBytesArray[0]!,
76
+ },
77
+ "0000000000000000000000000000000000000000000000000000000000000002": {
78
+ id: 1,
79
+ identifier:
80
+ "0000000000000000000000000000000000000000000000000000000000000002",
81
+ address: "https://1.spark.dev.dev.sparkinfra.net",
82
+
83
+ identityPublicKey: pubkeyBytesArray[1]!,
84
+ },
85
+ "0000000000000000000000000000000000000000000000000000000000000003": {
86
+ id: 2,
87
+ identifier:
88
+ "0000000000000000000000000000000000000000000000000000000000000003",
89
+ address: "https://2.spark.dev.dev.sparkinfra.net",
90
+ identityPublicKey: pubkeyBytesArray[2]!,
91
+ },
92
+ };
93
+ }
94
+
95
+ export function getLocalSigningOperators(): Record<string, SigningOperator> {
96
+ const pubkeys = [
97
+ "0322ca18fc489ae25418a0e768273c2c61cabb823edfb14feb891e9bec62016510",
98
+ "0341727a6c41b168f07eb50865ab8c397a53c7eef628ac1020956b705e43b6cb27",
99
+ "0305ab8d485cc752394de4981f8a5ae004f2becfea6f432c9a59d5022d8764f0a6",
100
+ "0352aef4d49439dedd798ac4aef1e7ebef95f569545b647a25338398c1247ffdea",
101
+ "02c05c88cc8fc181b1ba30006df6a4b0597de6490e24514fbdd0266d2b9cd3d0ba",
102
+ ];
103
+
104
+ const pubkeyBytesArray = pubkeys.map((pubkey) => hexToBytes(pubkey));
105
+
106
+ return {
107
+ "0000000000000000000000000000000000000000000000000000000000000001": {
108
+ id: 0,
109
+ identifier:
110
+ "0000000000000000000000000000000000000000000000000000000000000001",
111
+ address: "https://localhost:8535",
112
+ identityPublicKey: pubkeyBytesArray[0]!,
113
+ },
114
+ "0000000000000000000000000000000000000000000000000000000000000002": {
115
+ id: 1,
116
+ identifier:
117
+ "0000000000000000000000000000000000000000000000000000000000000002",
118
+ address: "https://localhost:8536",
119
+ identityPublicKey: pubkeyBytesArray[1]!,
120
+ },
121
+ "0000000000000000000000000000000000000000000000000000000000000003": {
122
+ id: 2,
123
+ identifier:
124
+ "0000000000000000000000000000000000000000000000000000000000000003",
125
+ address: "https://localhost:8537",
126
+ identityPublicKey: pubkeyBytesArray[2]!,
127
+ },
128
+ "0000000000000000000000000000000000000000000000000000000000000004": {
129
+ id: 3,
130
+ identifier:
131
+ "0000000000000000000000000000000000000000000000000000000000000004",
132
+ address: "https://localhost:8538",
133
+ identityPublicKey: pubkeyBytesArray[3]!,
134
+ },
135
+ "0000000000000000000000000000000000000000000000000000000000000005": {
136
+ id: 4,
137
+ identifier:
138
+ "0000000000000000000000000000000000000000000000000000000000000005",
139
+ address: "https://localhost:8539",
140
+ identityPublicKey: pubkeyBytesArray[4]!,
141
+ },
142
+ };
143
+ }
@@ -0,0 +1,532 @@
1
+ import {
2
+ bytesToHex,
3
+ bytesToNumberBE,
4
+ hexToBytes,
5
+ } from "@noble/curves/abstract/utils";
6
+ import { schnorr, secp256k1 } from "@noble/curves/secp256k1";
7
+ import { HDKey } from "@scure/bip32";
8
+ import * as bip39 from "@scure/bip39";
9
+ import { generateMnemonic } from "@scure/bip39";
10
+ import { wordlist } from "@scure/bip39/wordlists/english";
11
+ import { sha256 } from "@scure/btc-signer/utils";
12
+ import { Buffer } from "buffer";
13
+ import * as ecies from "eciesjs";
14
+ import { TreeNode } from "../proto/spark.js";
15
+ import { generateAdaptorFromSignature } from "../utils/adaptor-signature.js";
16
+ import { getMasterHDKeyFromSeed, subtractPrivateKeys } from "../utils/keys.js";
17
+ import { Network } from "../utils/network.js";
18
+ import {
19
+ splitSecretWithProofs,
20
+ VerifiableSecretShare,
21
+ } from "../utils/secret-sharing.js";
22
+ import {
23
+ createWasmSigningCommitment,
24
+ createWasmSigningNonce,
25
+ getRandomSigningNonce,
26
+ getSigningCommitmentFromNonce,
27
+ } from "../utils/signing.js";
28
+ import { aggregateFrost, signFrost } from "../utils/wasm.js";
29
+ import { KeyPackage } from "../wasm/spark_bindings.js";
30
+ globalThis.Buffer = Buffer;
31
+ export type SigningNonce = {
32
+ binding: Uint8Array;
33
+ hiding: Uint8Array;
34
+ };
35
+
36
+ export type SigningCommitment = {
37
+ binding: Uint8Array;
38
+ hiding: Uint8Array;
39
+ };
40
+
41
+ export type SignFrostParams = {
42
+ message: Uint8Array;
43
+ privateAsPubKey: Uint8Array;
44
+ publicKey: Uint8Array;
45
+ verifyingKey: Uint8Array;
46
+ selfCommitment: SigningCommitment;
47
+ statechainCommitments?: { [key: string]: SigningCommitment } | undefined;
48
+ adaptorPubKey?: Uint8Array | undefined;
49
+ };
50
+
51
+ export type AggregateFrostParams = Omit<SignFrostParams, "privateAsPubKey"> & {
52
+ selfSignature: Uint8Array;
53
+ statechainSignatures?: { [key: string]: Uint8Array } | undefined;
54
+ statechainPublicKeys?: { [key: string]: Uint8Array } | undefined;
55
+ };
56
+
57
+ export type SplitSecretWithProofsParams = {
58
+ secret: Uint8Array;
59
+ curveOrder: bigint;
60
+ threshold: number;
61
+ numShares: number;
62
+ isSecretPubkey?: boolean;
63
+ };
64
+
65
+ // TODO: Properly clean up keys when they are no longer needed
66
+ interface SparkSigner {
67
+ getIdentityPublicKey(): Promise<Uint8Array>;
68
+ getDepositSigningKey(): Promise<Uint8Array>;
69
+
70
+ generateMnemonic(): Promise<string>;
71
+ mnemonicToSeed(mnemonic: string): Promise<Uint8Array>;
72
+
73
+ createSparkWalletFromSeed(
74
+ seed: Uint8Array | string,
75
+ network: Network,
76
+ ): Promise<string>;
77
+
78
+ restoreSigningKeysFromLeafs(leafs: TreeNode[]): Promise<void>;
79
+ getTrackedPublicKeys(): Promise<Uint8Array[]>;
80
+ // Generates a new private key, and returns the public key
81
+ generatePublicKey(hash?: Uint8Array): Promise<Uint8Array>;
82
+ // Called when a public key is no longer needed
83
+ removePublicKey(publicKey: Uint8Array): Promise<void>;
84
+ getSchnorrPublicKey(publicKey: Uint8Array): Promise<Uint8Array>;
85
+
86
+ signSchnorr(message: Uint8Array, publicKey: Uint8Array): Promise<Uint8Array>;
87
+ signSchnorrWithIdentityKey(message: Uint8Array): Promise<Uint8Array>;
88
+
89
+ subtractPrivateKeysGivenPublicKeys(
90
+ first: Uint8Array,
91
+ second: Uint8Array,
92
+ ): Promise<Uint8Array>;
93
+ splitSecretWithProofs(
94
+ params: SplitSecretWithProofsParams,
95
+ ): Promise<VerifiableSecretShare[]>;
96
+
97
+ signFrost(params: SignFrostParams): Promise<Uint8Array>;
98
+ aggregateFrost(params: AggregateFrostParams): Promise<Uint8Array>;
99
+
100
+ signMessageWithPublicKey(
101
+ message: Uint8Array,
102
+ publicKey: Uint8Array,
103
+ compact?: boolean,
104
+ ): Promise<Uint8Array>;
105
+ // If compact is true, the signature should be in ecdsa compact format else it should be in DER format
106
+ signMessageWithIdentityKey(
107
+ message: Uint8Array,
108
+ compact?: boolean,
109
+ ): Promise<Uint8Array>;
110
+
111
+ encryptLeafPrivateKeyEcies(
112
+ receiverPublicKey: Uint8Array,
113
+ publicKey: Uint8Array,
114
+ ): Promise<Uint8Array>;
115
+ decryptEcies(ciphertext: Uint8Array): Promise<Uint8Array>;
116
+
117
+ getRandomSigningCommitment(): Promise<SigningCommitment>;
118
+ getSspIdentityPublicKey(network: Network): Promise<Uint8Array>;
119
+
120
+ hashRandomPrivateKey(): Promise<Uint8Array>;
121
+ generateAdaptorFromSignature(signature: Uint8Array): Promise<{
122
+ adaptorSignature: Uint8Array;
123
+ adaptorPublicKey: Uint8Array;
124
+ }>;
125
+
126
+ getDepositSigningKey(): Promise<Uint8Array>;
127
+ }
128
+
129
+ class DefaultSparkSigner implements SparkSigner {
130
+ private masterKey: HDKey | null = null;
131
+ private identityKey: HDKey | null = null;
132
+ private signingKey: HDKey | null = null;
133
+ private depositKey: HDKey | null = null;
134
+
135
+ // <hex, hex>
136
+ private publicKeyToPrivateKeyMap: Map<string, string> = new Map();
137
+
138
+ private commitmentToNonceMap: Map<SigningCommitment, SigningNonce> =
139
+ new Map();
140
+
141
+ private deriveSigningKey(hash: Uint8Array): Uint8Array {
142
+ if (!this.masterKey) {
143
+ throw new Error("Private key is not set");
144
+ }
145
+
146
+ const view = new DataView(hash.buffer);
147
+ const amount = (view.getUint32(0, false) % 0x80000000) + 0x80000000;
148
+
149
+ const newPrivateKey = this.signingKey?.deriveChild(amount).privateKey;
150
+
151
+ if (!newPrivateKey) {
152
+ throw new Error("Failed to recover signing key");
153
+ }
154
+
155
+ return newPrivateKey;
156
+ }
157
+
158
+ async restoreSigningKeysFromLeafs(leafs: TreeNode[]) {
159
+ if (!this.masterKey) {
160
+ throw new Error("Private key is not set");
161
+ }
162
+
163
+ for (const leaf of leafs) {
164
+ const hash = sha256(leaf.id);
165
+ const privateKey = this.deriveSigningKey(hash);
166
+
167
+ const publicKey = secp256k1.getPublicKey(privateKey);
168
+ this.publicKeyToPrivateKeyMap.set(
169
+ bytesToHex(publicKey),
170
+ bytesToHex(privateKey),
171
+ );
172
+ }
173
+ }
174
+
175
+ async getSchnorrPublicKey(publicKey: Uint8Array): Promise<Uint8Array> {
176
+ const privateKey = this.publicKeyToPrivateKeyMap.get(bytesToHex(publicKey));
177
+ if (!privateKey) {
178
+ throw new Error("Private key is not set");
179
+ }
180
+
181
+ return schnorr.getPublicKey(hexToBytes(privateKey));
182
+ }
183
+
184
+ async signSchnorr(
185
+ message: Uint8Array,
186
+ publicKey: Uint8Array,
187
+ ): Promise<Uint8Array> {
188
+ const privateKey = this.publicKeyToPrivateKeyMap.get(bytesToHex(publicKey));
189
+ if (!privateKey) {
190
+ throw new Error("Private key is not set");
191
+ }
192
+
193
+ return schnorr.sign(message, hexToBytes(privateKey));
194
+ }
195
+
196
+ async signSchnorrWithIdentityKey(message: Uint8Array): Promise<Uint8Array> {
197
+ if (!this.identityKey?.privateKey) {
198
+ throw new Error("Private key is not set");
199
+ }
200
+
201
+ const signature = schnorr.sign(message, this.identityKey.privateKey);
202
+
203
+ return signature;
204
+ }
205
+
206
+ async getIdentityPublicKey(): Promise<Uint8Array> {
207
+ if (!this.identityKey?.publicKey) {
208
+ throw new Error("Private key is not set");
209
+ }
210
+
211
+ return this.identityKey.publicKey;
212
+ }
213
+
214
+ async getDepositSigningKey(): Promise<Uint8Array> {
215
+ if (!this.depositKey?.publicKey) {
216
+ throw new Error("Deposit key is not set");
217
+ }
218
+
219
+ return this.depositKey.publicKey;
220
+ }
221
+
222
+ async generateMnemonic(): Promise<string> {
223
+ return generateMnemonic(wordlist);
224
+ }
225
+
226
+ async mnemonicToSeed(mnemonic: string): Promise<Uint8Array> {
227
+ return await bip39.mnemonicToSeed(mnemonic);
228
+ }
229
+
230
+ async getTrackedPublicKeys(): Promise<Uint8Array[]> {
231
+ return Array.from(this.publicKeyToPrivateKeyMap.keys()).map(hexToBytes);
232
+ }
233
+
234
+ async generatePublicKey(hash?: Uint8Array): Promise<Uint8Array> {
235
+ if (!this.masterKey) {
236
+ throw new Error("Private key is not set");
237
+ }
238
+
239
+ let newPrivateKey: Uint8Array | null = null;
240
+ if (hash) {
241
+ newPrivateKey = this.deriveSigningKey(hash);
242
+ } else {
243
+ newPrivateKey = secp256k1.utils.randomPrivateKey();
244
+ }
245
+
246
+ if (!newPrivateKey) {
247
+ throw new Error("Failed to generate new private key");
248
+ }
249
+
250
+ const publicKey = secp256k1.getPublicKey(newPrivateKey);
251
+
252
+ const pubKeyHex = bytesToHex(publicKey);
253
+ const privKeyHex = bytesToHex(newPrivateKey);
254
+ this.publicKeyToPrivateKeyMap.set(pubKeyHex, privKeyHex);
255
+
256
+ return publicKey;
257
+ }
258
+
259
+ async removePublicKey(publicKey: Uint8Array): Promise<void> {
260
+ this.publicKeyToPrivateKeyMap.delete(bytesToHex(publicKey));
261
+ }
262
+
263
+ async subtractPrivateKeysGivenPublicKeys(
264
+ first: Uint8Array,
265
+ second: Uint8Array,
266
+ ): Promise<Uint8Array> {
267
+ const firstPubKeyHex = bytesToHex(first);
268
+ const secondPubKeyHex = bytesToHex(second);
269
+
270
+ const firstPrivateKeyHex =
271
+ this.publicKeyToPrivateKeyMap.get(firstPubKeyHex);
272
+ const secondPrivateKeyHex =
273
+ this.publicKeyToPrivateKeyMap.get(secondPubKeyHex);
274
+
275
+ if (!firstPrivateKeyHex || !secondPrivateKeyHex) {
276
+ throw new Error("Private key is not set");
277
+ }
278
+
279
+ const firstPrivateKey = hexToBytes(firstPrivateKeyHex);
280
+ const secondPrivateKey = hexToBytes(secondPrivateKeyHex);
281
+
282
+ const resultPrivKey = subtractPrivateKeys(
283
+ firstPrivateKey,
284
+ secondPrivateKey,
285
+ );
286
+ const resultPubKey = secp256k1.getPublicKey(resultPrivKey);
287
+
288
+ const resultPrivKeyHex = bytesToHex(resultPrivKey);
289
+ const resultPubKeyHex = bytesToHex(resultPubKey);
290
+ this.publicKeyToPrivateKeyMap.set(resultPubKeyHex, resultPrivKeyHex);
291
+ return resultPubKey;
292
+ }
293
+
294
+ async splitSecretWithProofs({
295
+ secret,
296
+ curveOrder,
297
+ threshold,
298
+ numShares,
299
+ isSecretPubkey = false,
300
+ }: SplitSecretWithProofsParams): Promise<VerifiableSecretShare[]> {
301
+ if (isSecretPubkey) {
302
+ const pubKeyHex = bytesToHex(secret);
303
+ const privateKey = this.publicKeyToPrivateKeyMap.get(pubKeyHex);
304
+ if (!privateKey) {
305
+ throw new Error("Private key is not set");
306
+ }
307
+ secret = hexToBytes(privateKey);
308
+ }
309
+ const secretAsInt = bytesToNumberBE(secret);
310
+ return splitSecretWithProofs(secretAsInt, curveOrder, threshold, numShares);
311
+ }
312
+
313
+ async signFrost({
314
+ message,
315
+ privateAsPubKey,
316
+ publicKey,
317
+ verifyingKey,
318
+ selfCommitment,
319
+ statechainCommitments,
320
+ adaptorPubKey,
321
+ }: SignFrostParams): Promise<Uint8Array> {
322
+ const privateAsPubKeyHex = bytesToHex(privateAsPubKey);
323
+ const signingPrivateKey =
324
+ this.publicKeyToPrivateKeyMap.get(privateAsPubKeyHex);
325
+
326
+ if (!signingPrivateKey) {
327
+ throw new Error("Private key is not set");
328
+ }
329
+
330
+ const nonce = this.commitmentToNonceMap.get(selfCommitment);
331
+ if (!nonce) {
332
+ throw new Error("Nonce is not set");
333
+ }
334
+
335
+ const keyPackage = new KeyPackage(
336
+ hexToBytes(signingPrivateKey),
337
+ publicKey,
338
+ verifyingKey,
339
+ );
340
+
341
+ return signFrost({
342
+ msg: message,
343
+ keyPackage,
344
+ nonce: createWasmSigningNonce(nonce),
345
+ selfCommitment: createWasmSigningCommitment(selfCommitment),
346
+ statechainCommitments,
347
+ adaptorPubKey,
348
+ });
349
+ }
350
+
351
+ async aggregateFrost({
352
+ message,
353
+ publicKey,
354
+ verifyingKey,
355
+ selfCommitment,
356
+ statechainCommitments,
357
+ adaptorPubKey,
358
+ selfSignature,
359
+ statechainSignatures,
360
+ statechainPublicKeys,
361
+ }: AggregateFrostParams): Promise<Uint8Array> {
362
+ return aggregateFrost({
363
+ msg: message,
364
+ statechainSignatures,
365
+ statechainPublicKeys,
366
+ verifyingKey,
367
+ statechainCommitments,
368
+ selfCommitment: createWasmSigningCommitment(selfCommitment),
369
+ selfPublicKey: publicKey,
370
+ selfSignature,
371
+ adaptorPubKey,
372
+ });
373
+ }
374
+
375
+ async createSparkWalletFromSeed(
376
+ seed: Uint8Array | string,
377
+ network: Network,
378
+ ): Promise<string> {
379
+ if (typeof seed === "string") {
380
+ seed = hexToBytes(seed);
381
+ }
382
+
383
+ const hdkey = getMasterHDKeyFromSeed(seed);
384
+
385
+ if (!hdkey.privateKey || !hdkey.publicKey) {
386
+ throw new Error("Could not derive private key from seed");
387
+ }
388
+
389
+ const accountType = network === Network.REGTEST ? 0 : 1;
390
+ const identityKey = hdkey.derive(`m/8797555'/${accountType}'/0'`);
391
+ const signingKey = hdkey.derive(`m/8797555'/${accountType}'/1'`);
392
+ const depositKey = hdkey.derive(`m/8797555'/${accountType}'/2'`);
393
+
394
+ if (
395
+ !identityKey.privateKey ||
396
+ !depositKey.privateKey ||
397
+ !signingKey.privateKey ||
398
+ !identityKey.publicKey ||
399
+ !depositKey.publicKey ||
400
+ !signingKey.publicKey
401
+ ) {
402
+ throw new Error("Could not derive keys from seed");
403
+ }
404
+
405
+ this.masterKey = hdkey;
406
+ this.identityKey = identityKey;
407
+ this.depositKey = depositKey;
408
+ this.signingKey = signingKey;
409
+
410
+ this.publicKeyToPrivateKeyMap.set(
411
+ bytesToHex(identityKey.publicKey),
412
+ bytesToHex(identityKey.privateKey),
413
+ );
414
+ this.publicKeyToPrivateKeyMap.set(
415
+ bytesToHex(depositKey.publicKey),
416
+ bytesToHex(depositKey.privateKey),
417
+ );
418
+
419
+ return bytesToHex(identityKey.publicKey);
420
+ }
421
+
422
+ async signMessageWithPublicKey(
423
+ message: Uint8Array,
424
+ publicKey: Uint8Array,
425
+ compact?: boolean,
426
+ ): Promise<Uint8Array> {
427
+ const privateKey = this.publicKeyToPrivateKeyMap.get(bytesToHex(publicKey));
428
+ if (!privateKey) {
429
+ throw new Error(
430
+ `No private key found for public key: ${bytesToHex(publicKey)}`,
431
+ );
432
+ }
433
+
434
+ const signature = secp256k1.sign(message, hexToBytes(privateKey));
435
+
436
+ if (compact) {
437
+ return signature.toCompactRawBytes();
438
+ }
439
+
440
+ return signature.toDERRawBytes();
441
+ }
442
+
443
+ async signMessageWithIdentityKey(
444
+ message: Uint8Array,
445
+ compact?: boolean,
446
+ ): Promise<Uint8Array> {
447
+ if (!this.identityKey?.privateKey) {
448
+ throw new Error("Private key is not set");
449
+ }
450
+
451
+ const signature = secp256k1.sign(message, this.identityKey.privateKey);
452
+
453
+ if (compact) {
454
+ return signature.toCompactRawBytes();
455
+ }
456
+
457
+ return signature.toDERRawBytes();
458
+ }
459
+
460
+ async encryptLeafPrivateKeyEcies(
461
+ receiverPublicKey: Uint8Array,
462
+ publicKey: Uint8Array,
463
+ ): Promise<Uint8Array> {
464
+ const publicKeyHex = bytesToHex(publicKey);
465
+ const privateKey = this.publicKeyToPrivateKeyMap.get(publicKeyHex);
466
+ if (!privateKey) {
467
+ throw new Error("Private key is not set");
468
+ }
469
+
470
+ return ecies.encrypt(receiverPublicKey, hexToBytes(privateKey));
471
+ }
472
+
473
+ async decryptEcies(ciphertext: Uint8Array): Promise<Uint8Array> {
474
+ if (!this.identityKey?.privateKey) {
475
+ throw new Error("Private key is not set");
476
+ }
477
+ const receiverEciesPrivKey = ecies.PrivateKey.fromHex(
478
+ bytesToHex(this.identityKey.privateKey),
479
+ );
480
+ const privateKey = ecies.decrypt(receiverEciesPrivKey.toHex(), ciphertext);
481
+ const publicKey = secp256k1.getPublicKey(privateKey);
482
+ const publicKeyHex = bytesToHex(publicKey);
483
+ const privateKeyHex = bytesToHex(privateKey);
484
+ this.publicKeyToPrivateKeyMap.set(publicKeyHex, privateKeyHex);
485
+ return publicKey;
486
+ }
487
+
488
+ async getRandomSigningCommitment(): Promise<SigningCommitment> {
489
+ const nonce = getRandomSigningNonce();
490
+ const commitment = getSigningCommitmentFromNonce(nonce);
491
+ this.commitmentToNonceMap.set(commitment, nonce);
492
+ return commitment;
493
+ }
494
+
495
+ // Hardcode this for default ssp
496
+ async getSspIdentityPublicKey(network: Network): Promise<Uint8Array> {
497
+ if (network === Network.MAINNET) {
498
+ return hexToBytes(
499
+ "02e0b8d42c5d3b5fe4c5beb6ea796ab3bc8aaf28a3d3195407482c67e0b58228a5",
500
+ );
501
+ } else {
502
+ return hexToBytes(
503
+ "028c094a432d46a0ac95349d792c2e3730bd60c29188db716f56a99e39b95338b4",
504
+ );
505
+ }
506
+ }
507
+
508
+ async hashRandomPrivateKey(): Promise<Uint8Array> {
509
+ return sha256(secp256k1.utils.randomPrivateKey());
510
+ }
511
+
512
+ async generateAdaptorFromSignature(signature: Uint8Array): Promise<{
513
+ adaptorSignature: Uint8Array;
514
+ adaptorPublicKey: Uint8Array;
515
+ }> {
516
+ const adaptor = generateAdaptorFromSignature(signature);
517
+
518
+ const adaptorPublicKey = secp256k1.getPublicKey(adaptor.adaptorPrivateKey);
519
+
520
+ this.publicKeyToPrivateKeyMap.set(
521
+ bytesToHex(adaptorPublicKey),
522
+ bytesToHex(adaptor.adaptorPrivateKey),
523
+ );
524
+
525
+ return {
526
+ adaptorSignature: signature,
527
+ adaptorPublicKey: adaptorPublicKey,
528
+ };
529
+ }
530
+ }
531
+ export { DefaultSparkSigner };
532
+ export type { SparkSigner };