@leocuvee/turtlecoin-utils 0.0.14

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 (132) hide show
  1. package/.github/workflows/ci.yml +27 -0
  2. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  3. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  4. package/.idea/misc.xml +6 -0
  5. package/.idea/modules.xml +8 -0
  6. package/.idea/turtlecoin-utils.iml +12 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/.travis.yml +11 -0
  9. package/CONTRIBUTING.md +3 -0
  10. package/LICENSE +674 -0
  11. package/README.md +203 -0
  12. package/config.json +7 -0
  13. package/docs/.nojekyll +0 -0
  14. package/docs/CNAME +1 -0
  15. package/docs/assets/css/main.css +2321 -0
  16. package/docs/assets/images/icons.png +0 -0
  17. package/docs/assets/images/icons@2x.png +0 -0
  18. package/docs/assets/images/widgets.png +0 -0
  19. package/docs/assets/images/widgets@2x.png +0 -0
  20. package/docs/assets/js/main.js +1 -0
  21. package/docs/assets/js/search.js +3 -0
  22. package/docs/classes/address.html +964 -0
  23. package/docs/classes/addressprefix.html +431 -0
  24. package/docs/classes/block.html +965 -0
  25. package/docs/classes/blocktemplate.html +695 -0
  26. package/docs/classes/cryptonote.html +1137 -0
  27. package/docs/classes/ed25519.keypair.html +400 -0
  28. package/docs/classes/ed25519.keys.html +373 -0
  29. package/docs/classes/extranoncetag.extranoncedata.html +454 -0
  30. package/docs/classes/extranoncetag.extranoncepaymentid.html +453 -0
  31. package/docs/classes/extranoncetag.iextranonce.html +347 -0
  32. package/docs/classes/extratag.extramergedmining.html +494 -0
  33. package/docs/classes/extratag.extranonce.html +530 -0
  34. package/docs/classes/extratag.extrapadding.html +456 -0
  35. package/docs/classes/extratag.extrapublickey.html +460 -0
  36. package/docs/classes/extratag.iextratag.html +355 -0
  37. package/docs/classes/levinpacket.html +674 -0
  38. package/docs/classes/levinpayloads.handshake.html +731 -0
  39. package/docs/classes/levinpayloads.ilevinpayload.html +318 -0
  40. package/docs/classes/levinpayloads.liteblock.html +494 -0
  41. package/docs/classes/levinpayloads.missingtransactions.html +494 -0
  42. package/docs/classes/levinpayloads.newblock.html +540 -0
  43. package/docs/classes/levinpayloads.newtransactions.html +402 -0
  44. package/docs/classes/levinpayloads.peerentry.html +610 -0
  45. package/docs/classes/levinpayloads.ping.html +450 -0
  46. package/docs/classes/levinpayloads.rawblock.html +344 -0
  47. package/docs/classes/levinpayloads.requestchain.html +402 -0
  48. package/docs/classes/levinpayloads.requestgetobjects.html +448 -0
  49. package/docs/classes/levinpayloads.requesttxpool.html +402 -0
  50. package/docs/classes/levinpayloads.responsechain.html +494 -0
  51. package/docs/classes/levinpayloads.responsegetobjects.html +540 -0
  52. package/docs/classes/levinpayloads.timedsync.html +540 -0
  53. package/docs/classes/multisig.html +930 -0
  54. package/docs/classes/multisigmessage.html +694 -0
  55. package/docs/classes/parentblock.html +347 -0
  56. package/docs/classes/transaction.html +925 -0
  57. package/docs/classes/transactioninputs.coinbaseinput.html +390 -0
  58. package/docs/classes/transactioninputs.itransactioninput.html +321 -0
  59. package/docs/classes/transactioninputs.keyinput.html +459 -0
  60. package/docs/classes/transactionoutputs.itransactionoutput.html +317 -0
  61. package/docs/classes/transactionoutputs.keyoutput.html +422 -0
  62. package/docs/enums/extranoncetag.noncetagtype.html +246 -0
  63. package/docs/enums/extratag.extratagtype.html +280 -0
  64. package/docs/enums/levinprotocol.commandtype.html +391 -0
  65. package/docs/enums/transactioninputs.inputtype.html +246 -0
  66. package/docs/enums/transactionoutputs.outputtype.html +229 -0
  67. package/docs/globals.html +238 -0
  68. package/docs/index.html +271 -0
  69. package/docs/interfaces/interfaces.config.html +590 -0
  70. package/docs/interfaces/interfaces.daemonblocktemplateresponse.html +323 -0
  71. package/docs/interfaces/interfaces.generatedinput.html +304 -0
  72. package/docs/interfaces/interfaces.generatedoutput.html +285 -0
  73. package/docs/interfaces/interfaces.inputkeys.html +304 -0
  74. package/docs/interfaces/interfaces.ipreparedtransaction.html +268 -0
  75. package/docs/interfaces/interfaces.output.html +399 -0
  76. package/docs/interfaces/interfaces.preparedringsignature.html +377 -0
  77. package/docs/interfaces/interfaces.preparedtransaction.html +329 -0
  78. package/docs/interfaces/interfaces.randomoutput.html +285 -0
  79. package/docs/interfaces/interfaces.transactionrecipient.html +285 -0
  80. package/docs/interfaces/multisiginterfaces.partialkeyimage.html +277 -0
  81. package/docs/interfaces/multisiginterfaces.partialsigningkey.html +277 -0
  82. package/docs/modules/ed25519.html +195 -0
  83. package/docs/modules/extranoncetag.html +208 -0
  84. package/docs/modules/extratag.html +216 -0
  85. package/docs/modules/interfaces.html +231 -0
  86. package/docs/modules/levinpayloads.html +247 -0
  87. package/docs/modules/levinprotocol.html +191 -0
  88. package/docs/modules/multisiginterfaces.html +195 -0
  89. package/docs/modules/transactioninputs.html +208 -0
  90. package/docs/modules/transactionoutputs.html +204 -0
  91. package/index.d.ts +417 -0
  92. package/index.js +1508 -0
  93. package/lib/base58.js +220 -0
  94. package/lib/biginteger.js +1591 -0
  95. package/lib/blocktemplate.js +408 -0
  96. package/lib/crypto.js +19698 -0
  97. package/lib/mnemonic.js +1204 -0
  98. package/lib/nacl-fast-cn.js +608 -0
  99. package/lib/ringsigs.js +24262 -0
  100. package/lib/sha3.js +477 -0
  101. package/package.json +58 -0
  102. package/src/Address.ts +433 -0
  103. package/src/AddressPrefix.ts +117 -0
  104. package/src/Block.ts +556 -0
  105. package/src/BlockTemplate.ts +289 -0
  106. package/src/Common.ts +105 -0
  107. package/src/Config.ts +66 -0
  108. package/src/CryptoNote.ts +1072 -0
  109. package/src/LevinPacket.ts +366 -0
  110. package/src/Multisig.ts +600 -0
  111. package/src/MultisigMessage.ts +374 -0
  112. package/src/ParentBlock.ts +39 -0
  113. package/src/Transaction.ts +628 -0
  114. package/src/Types/ED25519.ts +187 -0
  115. package/src/Types/IExtraNonce.ts +225 -0
  116. package/src/Types/IExtraTag.ts +507 -0
  117. package/src/Types/ITransaction.ts +230 -0
  118. package/src/Types/ITransactionInput.ts +190 -0
  119. package/src/Types/ITransactionOutput.ts +108 -0
  120. package/src/Types/LevinPayloads.ts +1576 -0
  121. package/src/Types/MultisigInterfaces.ts +65 -0
  122. package/src/Types/PortableStorage.ts +289 -0
  123. package/src/Types.ts +36 -0
  124. package/src/index.ts +36 -0
  125. package/test/template.json +6 -0
  126. package/test/test.js +1457 -0
  127. package/tests/blocktemplate.json +6 -0
  128. package/tests/tests.js +215 -0
  129. package/tsconfig.json +15 -0
  130. package/tslint.json +36 -0
  131. package/typedoc.json +10 -0
  132. package/webpack.config.js +15 -0
@@ -0,0 +1,600 @@
1
+ // Copyright (c) 2020, The TurtleCoin Developers
2
+ //
3
+ // Please see the included LICENSE file for more information.
4
+
5
+ import {Address} from './Address';
6
+ import {ED25519} from './Types/ED25519';
7
+ import {Interfaces, MultisigInterfaces, TransactionInputs, TurtleCoinCrypto} from './Types';
8
+ import {Transaction} from './Transaction';
9
+ /** @ignore */
10
+ import KeyPair = ED25519.KeyPair;
11
+
12
+ /**
13
+ * Represents a multisig helper class that can be used for the creation of multisig wallets
14
+ */
15
+ export class Multisig {
16
+
17
+ /**
18
+ * Returns an address object representing the multisig wallet address
19
+ */
20
+ public get address(): Address {
21
+ if (!this.isReady) {
22
+ throw new Error('Not all participants have been loaded');
23
+ }
24
+
25
+ return Address.fromViewOnlyKeys(this.spend, this.view);
26
+ }
27
+
28
+ /**
29
+ * Returns the threshold (M) of the multisig wallet
30
+ */
31
+ public get threshold(): number {
32
+ return this.m_threshold;
33
+ }
34
+
35
+ /**
36
+ * Returns the participants (N) of the multisig wallet
37
+ */
38
+ public get participants(): number {
39
+ return this.m_participants;
40
+ }
41
+
42
+ /**
43
+ * Returns the number of participants currently loaded into the object
44
+ */
45
+ public get current_participants(): number {
46
+ return this.m_currentParticipants;
47
+ }
48
+
49
+ /**
50
+ * Returns the shared private view key of the multisig wallet
51
+ */
52
+ public get view(): string {
53
+ if (!this.isViewReady) {
54
+ throw new Error('Not all participants have been loaded');
55
+ }
56
+
57
+ return TurtleCoinCrypto.calculateSharedPrivateKey(this.m_view_keys);
58
+ }
59
+
60
+ /**
61
+ * Returns the shared public spend key of the multisig wallet
62
+ */
63
+ public get spend(): string {
64
+ if (!this.isSpendReady) {
65
+ throw new Error('Not all participants have been loaded');
66
+ }
67
+
68
+ const keys: string[] = this.m_participant_keys;
69
+
70
+ this.m_multisig_keys.forEach((key) => {
71
+ if (key.publicKey.length !== 0 &&
72
+ keys.indexOf(key.publicKey) === -1
73
+ ) {
74
+ keys.push(key.publicKey);
75
+ }
76
+ });
77
+
78
+ return TurtleCoinCrypto.calculateSharedPublicKey(keys);
79
+ }
80
+
81
+ /**
82
+ * Returns if the object is ready for export and/or use
83
+ */
84
+ public get isReady(): boolean {
85
+ return (this.isViewReady && this.isSpendReady);
86
+ }
87
+
88
+ /**
89
+ * Returns our multisig keys
90
+ */
91
+ public get multisig_keys(): KeyPair[] {
92
+ return this.m_multisig_keys;
93
+ }
94
+
95
+ /**
96
+ * Returns the public multisig keys
97
+ */
98
+ public get public_multisig_keys(): string[] {
99
+ const result: string[] = [];
100
+
101
+ this.calculated_multisig_keys.forEach((key) => {
102
+ result.push(key.publicKey);
103
+ });
104
+
105
+ return result;
106
+ }
107
+
108
+ /**
109
+ * Returns the private multisig keys
110
+ */
111
+ public get private_multisig_keys(): string [] {
112
+ const result: string[] = [];
113
+
114
+ this.calculated_multisig_keys.forEach((key) => {
115
+ result.push(key.privateKey);
116
+ });
117
+
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Calculates our multisig keys using the participant public spend keys
123
+ * @returns our multisig keys key pairs
124
+ */
125
+ private get calculated_multisig_keys(): KeyPair[] {
126
+ if (this.m_threshold !== this.m_participants) {
127
+ const multisig_keys: KeyPair[] = [];
128
+
129
+ this.m_wallet_multisig_keys.forEach((multisig_key) => {
130
+ const keys =
131
+ TurtleCoinCrypto.calculateMultisigPrivateKeys(multisig_key.privateKey, this.m_participant_keys);
132
+
133
+ keys.forEach((key) => {
134
+ multisig_keys.push(new KeyPair(undefined, key));
135
+ });
136
+ });
137
+
138
+ return multisig_keys;
139
+ }
140
+
141
+ throw new Error('Not a M:N instance');
142
+ }
143
+
144
+ /**
145
+ * Returns if the view information is ready
146
+ */
147
+ private get isViewReady(): boolean {
148
+ return (this.m_currentParticipants === this.m_participants);
149
+ }
150
+
151
+ /**
152
+ * Returns if the spend information is ready
153
+ */
154
+ private get isSpendReady(): boolean {
155
+ const loaded = (this.threshold === this.participants) ?
156
+ this.m_participant_keys.length + 1 :
157
+ this.m_participant_keys.length;
158
+ return (loaded === Multisig.requiredSigningKeys(this.threshold, this.participants));
159
+ }
160
+
161
+ /**
162
+ * Let's us know how many times participants must exchange information after the initial exchange
163
+ * @param threshold the number of participants required to construct a new transaction
164
+ * @param participants the wallet participants
165
+ * @returns the number of additional exchange rounds required
166
+ */
167
+ public static exchangeRoundsRequired(threshold: number, participants: number): number {
168
+ return participants - threshold;
169
+ }
170
+
171
+ /**
172
+ * Initializes an initial multisig object using our address information
173
+ * @param wallet our base wallet used to create the wallet
174
+ * @param threshold the number of participants required to construct a new transaction
175
+ * @param participants the wallet participants
176
+ * @returns a new instance of the object
177
+ */
178
+ public static fromAddress(wallet: Address, threshold: number, participants: number): Multisig {
179
+ if (!isValidThreshold(threshold, participants)) {
180
+ throw new Error('Threshold does not require a majority of participants');
181
+ }
182
+
183
+ const result = new Multisig();
184
+
185
+ if (!wallet.spend.isPaired) {
186
+ throw new Error('Must have both private and public spend keys');
187
+ }
188
+
189
+ if (!wallet.view.isPaired) {
190
+ throw new Error('Must have both private and public view keys');
191
+ }
192
+
193
+ result.m_wallet_multisig_keys.push(wallet.spend);
194
+
195
+ result.m_multisig_keys.push(wallet.spend);
196
+
197
+ result.m_view_keys.push(wallet.view.privateKey);
198
+
199
+ result.m_threshold = threshold;
200
+
201
+ result.m_participants = participants;
202
+
203
+ return result;
204
+ }
205
+
206
+ /**
207
+ * Initializes a multisig object using our previously generated multisig private keys
208
+ * @param multisig_private_keys the previously generated multisig private keys
209
+ * @param sharedPrivateViewKey the previously calculated view private key
210
+ * @param threshold the number of participants required to construct a new transaction
211
+ * @param participants the wallet participants
212
+ * @returns a new instance of the object
213
+ */
214
+ public static fromMultisigKeys(
215
+ multisig_private_keys: string[],
216
+ sharedPrivateViewKey: string,
217
+ threshold: number,
218
+ participants: number,
219
+ ): Multisig {
220
+ if (!isValidThreshold(threshold, participants)) {
221
+ throw new Error('Threshold does not require a majority of participants');
222
+ }
223
+
224
+ const result = new Multisig();
225
+
226
+ multisig_private_keys.forEach((key) => {
227
+ if (!TurtleCoinCrypto.checkScalar(key)) {
228
+ throw new Error('Found an invalid private key in the list of multisig private keys');
229
+ }
230
+
231
+ result.m_multisig_keys.push(new KeyPair(undefined, key));
232
+
233
+ result.m_wallet_multisig_keys.push(new KeyPair(undefined, key));
234
+ });
235
+
236
+ if (!TurtleCoinCrypto.checkScalar(sharedPrivateViewKey)) {
237
+ throw new Error('Private view key is not a valid private key');
238
+ }
239
+
240
+ result.m_view_keys.push(sharedPrivateViewKey);
241
+
242
+ result.m_threshold = threshold;
243
+
244
+ result.m_participants = participants;
245
+
246
+ return result;
247
+ }
248
+
249
+ /**
250
+ * Returns the total number of signing keys created using the M:N values supplied
251
+ * @param threshold the number of participants required to construct a new transaction
252
+ * @param participants the wallet participants
253
+ * @returns the total number of signing keys created
254
+ */
255
+ public static requiredSigningKeys(threshold: number, participants: number): number {
256
+ return required_keys(threshold, participants);
257
+ }
258
+
259
+ /**
260
+ * Returns if the given M:N scheme is valid for this library
261
+ * @param threshold the number of participants required to construct a new transaction
262
+ * @param participants the wallet participants
263
+ * @returns if the given scheme is valid for this library
264
+ */
265
+ public static isValidThreshold(threshold: number, participants: number): boolean {
266
+ return isValidThreshold(threshold, participants);
267
+ }
268
+
269
+ /**
270
+ * Restores a key image from partial key images
271
+ * @param publicEphemeral the key image public emphermal
272
+ * @param derivation the key input derivation
273
+ * @param outputIndex the key input output index
274
+ * @param partialKeyImages the partial key images
275
+ * @returns the restored key image
276
+ */
277
+ public static async restoreKeyImage(
278
+ publicEphemeral: string,
279
+ derivation: string,
280
+ outputIndex: number,
281
+ partialKeyImages: string[],
282
+ ): Promise<string> {
283
+ return TurtleCoinCrypto.restoreKeyImage(publicEphemeral, derivation, outputIndex, partialKeyImages);
284
+ }
285
+
286
+ private m_wallet_multisig_keys: KeyPair[] = [];
287
+ private m_multisig_keys: KeyPair[] = [];
288
+ private m_participant_keys: string[] = [];
289
+ private m_view_keys: string[] = [];
290
+ private m_threshold: number = 0;
291
+ private m_participants: number = 0;
292
+ private m_currentParticipants: number = 1;
293
+
294
+ /**
295
+ * Adds a participant to the multisig object
296
+ * Note: If this is an additional round of exchange, the publicSpendKeys should be the array of
297
+ * public multisig keys of the participant and we will not supply the private view key again.
298
+ * @param publicSpendKeys the participant spend key(s)
299
+ * @param [privateViewKey] the private view key of the participant
300
+ */
301
+ public async addParticipant(
302
+ publicSpendKeys: string[] | string,
303
+ privateViewKey?: string,
304
+ ): Promise<void> {
305
+ if (privateViewKey && !TurtleCoinCrypto.checkScalar(privateViewKey)) {
306
+ throw new Error('Private view key is not a valid private key');
307
+ }
308
+
309
+ if (Array.isArray(publicSpendKeys) && publicSpendKeys.length > 1 && privateViewKey) {
310
+ throw new Error('Must not supply the private view key with the participant in subsequent rounds');
311
+ }
312
+
313
+ if (!Array.isArray(publicSpendKeys)) {
314
+ publicSpendKeys = [publicSpendKeys];
315
+ }
316
+
317
+ publicSpendKeys.forEach((key) => {
318
+ if (!TurtleCoinCrypto.checkKey(key)) {
319
+ throw new Error('Found an invalid public spend key in the list');
320
+ }
321
+ });
322
+
323
+ if (privateViewKey && this.m_view_keys.indexOf(privateViewKey) === -1) {
324
+ this.m_view_keys.push(privateViewKey);
325
+ }
326
+
327
+ publicSpendKeys.forEach((key) => {
328
+ if (this.m_participant_keys.indexOf(key) === -1) {
329
+ this.m_participant_keys.push(key);
330
+ }
331
+ });
332
+
333
+ this.m_currentParticipants++;
334
+ }
335
+
336
+ /**
337
+ * Generates the partial key images for the given public ephemeral
338
+ * @param transactionHash the transaction hash containing the output used
339
+ * @param publicEphemeral the public ephemeral of the output
340
+ * @param outputIndex the index of the output in the transaction
341
+ * @returns the partial key images
342
+ */
343
+ public async generatePartialKeyImages(
344
+ transactionHash: string,
345
+ publicEphemeral: string,
346
+ outputIndex: number,
347
+ ): Promise<MultisigInterfaces.PartialKeyImage[]> {
348
+ const promises = [];
349
+
350
+ for (const multisigKey of this.m_multisig_keys) {
351
+ promises.push(TurtleCoinCrypto.generateKeyImage(publicEphemeral, multisigKey.privateKey));
352
+ }
353
+
354
+ const results = await Promise.all(promises);
355
+
356
+ const partialKeyImages: MultisigInterfaces.PartialKeyImage[] = [];
357
+
358
+ for (const result of results) {
359
+ partialKeyImages.push({
360
+ transactionHash,
361
+ outputIndex,
362
+ partialKeyImage: result,
363
+ });
364
+ }
365
+
366
+ return partialKeyImages;
367
+ }
368
+
369
+ /**
370
+ * Generates the partial signing keys for the given prepared transaction
371
+ * @param tx the prepared transaction
372
+ * @returns the partial signing keys
373
+ */
374
+ public async generatePartialSigningKeys(
375
+ tx: Interfaces.PreparedTransaction,
376
+ ): Promise<MultisigInterfaces.PartialSigningKey[]> {
377
+ const promises = [];
378
+
379
+ for (let i = 0; i < tx.transaction.signatures.length; i++) {
380
+ const realOutputIndex = getRealOutputIndex(tx, i);
381
+
382
+ for (const multisigKey of this.m_multisig_keys) {
383
+ promises.push(
384
+ generatePartialSigningKey(
385
+ tx.transaction.signatures[i][realOutputIndex], i, multisigKey.privateKey));
386
+ }
387
+ }
388
+
389
+ const results = await Promise.all(promises);
390
+
391
+ const partialSigningKeys: MultisigInterfaces.PartialSigningKey[] = [];
392
+
393
+ for (const result of results) {
394
+ partialSigningKeys.push({
395
+ transactionPrefixHash: tx.transaction.prefixHash,
396
+ index: result.index,
397
+ partialSigningKey: result.key,
398
+ });
399
+ }
400
+
401
+ return partialSigningKeys;
402
+ }
403
+
404
+ /**
405
+ * Restores the ring signatures of a prepared transaction using the supplied partial signing keys
406
+ * @param tx the prepared transaction
407
+ * @param partialSigningKeys the partial signing keys required for the signature scheme
408
+ * @returns the completed transaction
409
+ */
410
+ public async completeTransaction(
411
+ tx: Interfaces.PreparedTransaction,
412
+ partialSigningKeys: MultisigInterfaces.PartialSigningKey[],
413
+ ): Promise<Transaction> {
414
+ const promises = [];
415
+
416
+ for (let i = 0; i < tx.transaction.signatures.length; i++) {
417
+ const preparedRingSignature = getPreparedRingSignature(tx.signatureMeta, i);
418
+ const ringPartialKeys = getPartialSigningKeys(partialSigningKeys, i);
419
+
420
+ for (const partialKey of ringPartialKeys) {
421
+ promises.push(restoreRingSignatures(
422
+ preparedRingSignature.input.derivation,
423
+ preparedRingSignature.input.outputIndex,
424
+ ringPartialKeys,
425
+ preparedRingSignature.realOutputIndex,
426
+ preparedRingSignature.key,
427
+ tx.transaction.signatures[i],
428
+ i,
429
+ ));
430
+ }
431
+ }
432
+
433
+ const results = await Promise.all(promises);
434
+
435
+ for (const result of results) {
436
+ tx.transaction.signatures[result.index] = result.sigs;
437
+ }
438
+
439
+ const prefixHash = tx.transaction.prefixHash;
440
+
441
+ const checkPromises = [];
442
+
443
+ for (let i = 0; i < tx.transaction.inputs.length; i++) {
444
+ checkPromises.push(checkRingSignatures(
445
+ prefixHash,
446
+ (tx.transaction.inputs[i] as TransactionInputs.KeyInput).keyImage,
447
+ getInputKeys(tx.signatureMeta, i),
448
+ tx.transaction.signatures[i],
449
+ ));
450
+ }
451
+
452
+ const validSigs = await Promise.all(checkPromises);
453
+
454
+ for (const valid of validSigs) {
455
+ if (!valid) {
456
+ throw new Error('Could not complete ring signatures');
457
+ }
458
+ }
459
+
460
+ return tx.transaction;
461
+ }
462
+ }
463
+
464
+ /** @ignore */
465
+ function isValidThreshold(threshold: number, participants: number): boolean {
466
+ return (!((threshold / participants) <= .5) && required_keys(threshold, participants) <= 2 ** 13);
467
+ }
468
+
469
+ /** @ignore */
470
+ function required_keys(
471
+ threshold: number,
472
+ participants: number,
473
+ ): number {
474
+ let result = participants;
475
+
476
+ const rounds = Multisig.exchangeRoundsRequired(threshold, participants);
477
+
478
+ for (let i = 0; i < rounds; i++) {
479
+ result = result - 1;
480
+
481
+ result = ((result ** 2) + result) / 2;
482
+ }
483
+
484
+ return result;
485
+ }
486
+
487
+ /** @ignore */
488
+ async function generatePartialSigningKey(
489
+ preparedSignature: string,
490
+ index: number,
491
+ privateSpendKey: string,
492
+ ): Promise<{ key: string, index: number }> {
493
+ const key = await TurtleCoinCrypto.generatePartialSigningKey(preparedSignature, privateSpendKey);
494
+
495
+ return {
496
+ key,
497
+ index,
498
+ };
499
+ }
500
+
501
+ /** @ignore */
502
+ function getRealOutputIndex(
503
+ tx: Interfaces.PreparedTransaction,
504
+ index: number,
505
+ ): number {
506
+ for (const sigs of tx.signatureMeta) {
507
+ if (sigs.index === index) {
508
+ return sigs.realOutputIndex;
509
+ }
510
+ }
511
+
512
+ throw new Error('Could not find the real output index in the prepared ring signatures');
513
+ }
514
+
515
+ /** @ignore */
516
+ function getPartialSigningKeys(
517
+ partialSigningKeys: MultisigInterfaces.PartialSigningKey[],
518
+ index: number,
519
+ ): MultisigInterfaces.PartialSigningKey[] {
520
+ const results: MultisigInterfaces.PartialSigningKey[] = [];
521
+
522
+ for (const partialSigningKey of partialSigningKeys) {
523
+ if (partialSigningKey.index === index) {
524
+ results.push(partialSigningKey);
525
+ }
526
+ }
527
+
528
+ return results;
529
+ }
530
+
531
+ /** @ignore */
532
+ function getPreparedRingSignature(
533
+ preparedRingSignatures: Interfaces.PreparedRingSignature[],
534
+ index: number,
535
+ ): Interfaces.PreparedRingSignature {
536
+ for (const preparedRingSignature of preparedRingSignatures) {
537
+ if (preparedRingSignature.index === index) {
538
+ return preparedRingSignature;
539
+ }
540
+ }
541
+
542
+ throw new Error('Prepared ring signature not found at specified index');
543
+ }
544
+
545
+ /** @ignore */
546
+ async function restoreRingSignatures(
547
+ derivation: string,
548
+ outputIndex: number,
549
+ partialSigningKeys: MultisigInterfaces.PartialSigningKey[],
550
+ realOutputIndex: number,
551
+ key: string,
552
+ signatures: string[],
553
+ index: number,
554
+ ): Promise<{ sigs: string[], index: number }> {
555
+ const keys: string[] = [];
556
+
557
+ for (const partialSigningKey of partialSigningKeys) {
558
+ if (partialSigningKey.index !== index) {
559
+ throw new Error('invalid partial signing key supplied');
560
+ }
561
+ keys.push(partialSigningKey.partialSigningKey);
562
+ }
563
+
564
+ const sigs = await TurtleCoinCrypto.restoreRingSignatures(
565
+ derivation,
566
+ outputIndex,
567
+ keys,
568
+ realOutputIndex,
569
+ key,
570
+ signatures,
571
+ );
572
+
573
+ return {
574
+ sigs,
575
+ index,
576
+ };
577
+ }
578
+
579
+ /** @ignore */
580
+ function getInputKeys(preparedSignatures: Interfaces.PreparedRingSignature[], index: number): string[] {
581
+ for (const meta of preparedSignatures) {
582
+ if (meta.index === index) {
583
+ if (meta.inputKeys) {
584
+ return meta.inputKeys;
585
+ }
586
+ }
587
+ }
588
+
589
+ throw new Error('Could not locate input keys in the prepared signatures');
590
+ }
591
+
592
+ /** @ignore */
593
+ async function checkRingSignatures(
594
+ hash: string,
595
+ keyImage: string,
596
+ publicKeys: string[],
597
+ signatures: string[],
598
+ ): Promise<boolean> {
599
+ return TurtleCoinCrypto.checkRingSignatures(hash, keyImage, publicKeys, signatures);
600
+ }