@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,628 @@
1
+ // Copyright (c) 2018-2020, The TurtleCoin Developers
2
+ //
3
+ // Please see the included LICENSE file for more information.
4
+
5
+ import {Common} from './Common';
6
+ import {
7
+ BigInteger,
8
+ ED25519,
9
+ ExtraNonceTag,
10
+ ExtraTag,
11
+ TransactionInputs,
12
+ TransactionOutputs,
13
+ TurtleCoinCrypto,
14
+ } from './Types';
15
+ import {Reader, Writer} from 'bytestream-helper';
16
+
17
+ /** @ignore */
18
+ const TransactionVersion2Suffix = 'bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a0000000000000000000000000000000000000000000000000000000000000000';
19
+
20
+ /** @ignore */
21
+ interface Cache {
22
+ prefix: string;
23
+ blob: string;
24
+ prefixHash: string;
25
+ hash: string;
26
+ }
27
+
28
+ /**
29
+ * Represents a TurtleCoin Transaction
30
+ */
31
+ export class Transaction {
32
+
33
+ /**
34
+ * Returns the total amount of the transaction inputs
35
+ */
36
+ public get amount(): number {
37
+ const amount = BigInteger.zero;
38
+
39
+ for (const input of this.inputs) {
40
+ if (input.type === TransactionInputs.InputType.KEY) {
41
+ amount.add((input as TransactionInputs.KeyInput).amount);
42
+ }
43
+ }
44
+
45
+ return amount.toJSNumber();
46
+ }
47
+
48
+ /**
49
+ * Returns the transaction extra as a buffer
50
+ */
51
+ public get extra(): Buffer {
52
+ if (!this.m_readonly) {
53
+ return writeExtra(this.m_extra);
54
+ }
55
+
56
+ return this.m_rawExtra;
57
+ }
58
+
59
+ /**
60
+ * Returns the structured arbitrary data found in the transaction extra
61
+ */
62
+ public get extraData(): Buffer {
63
+ let result = Buffer.alloc(0);
64
+
65
+ for (const tag of this.m_extra) {
66
+ if (tag.tag === ExtraTag.ExtraTagType.NONCE) {
67
+ const innerTag = tag as ExtraTag.ExtraNonce;
68
+ for (const subTag of innerTag.tags) {
69
+ if (subTag.tag === ExtraNonceTag.NonceTagType.EXTRA_DATA) {
70
+ result = (subTag as ExtraNonceTag.ExtraNonceData).data;
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ /**
80
+ * Returns the fee of the transaction
81
+ */
82
+ public get fee(): number {
83
+ const inputAmount = BigInteger(this.amount);
84
+
85
+ if (inputAmount === BigInteger.zero) {
86
+ return 0;
87
+ }
88
+
89
+ const outputAmount = BigInteger.zero;
90
+
91
+ for (const output of this.outputs) {
92
+ if (output.type === TransactionOutputs.OutputType.KEY) {
93
+ outputAmount.add((output as TransactionOutputs.KeyOutput).amount);
94
+ }
95
+ }
96
+
97
+ return inputAmount.subtract(outputAmount).toJSNumber();
98
+ }
99
+
100
+ /**
101
+ * Returns the transaction hash
102
+ */
103
+ public get hash(): string {
104
+ if (this.m_cached.blob && this.m_cached.blob === this.toString() && this.m_cached.hash) {
105
+ return this.m_cached.hash;
106
+ }
107
+
108
+ this.m_cached.blob = this.toString();
109
+
110
+ const hash = TurtleCoinCrypto.cn_fast_hash(this.m_cached.blob);
111
+
112
+ if (this.version >= 2) {
113
+ const hash2 = TurtleCoinCrypto.cn_fast_hash(hash + TransactionVersion2Suffix);
114
+
115
+ this.m_cached.hash = hash2;
116
+
117
+ return hash2;
118
+ }
119
+
120
+ this.m_cached.hash = hash;
121
+
122
+ return hash;
123
+ }
124
+
125
+ /**
126
+ * Returns the merged mining tag found within the transaction
127
+ */
128
+ public get mergedMining(): ExtraTag.ExtraMergedMining | undefined {
129
+ let result;
130
+
131
+ for (const tag of this.m_extra) {
132
+ if (tag.tag === ExtraTag.ExtraTagType.MERGED_MINING) {
133
+ result = tag as ExtraTag.ExtraMergedMining;
134
+ }
135
+ }
136
+
137
+ return result;
138
+ }
139
+
140
+ /**
141
+ * Returns the payment ID found within the transaction
142
+ */
143
+ public get paymentId(): string | undefined {
144
+ let result;
145
+
146
+ for (const tag of this.m_extra) {
147
+ if (tag.tag === ExtraTag.ExtraTagType.NONCE) {
148
+ const innerTag = tag as ExtraTag.ExtraNonce;
149
+ for (const subTag of innerTag.tags) {
150
+ if (subTag.tag === ExtraNonceTag.NonceTagType.PAYMENT_ID) {
151
+ result = (subTag as ExtraNonceTag.ExtraNoncePaymentId).paymentId;
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ return result;
158
+ }
159
+
160
+ /**
161
+ * Returns the transaction prefix in hexadecimal (blob) form
162
+ */
163
+ public get prefix(): string {
164
+ return this.toBuffer(true).toString('hex');
165
+ }
166
+
167
+ /**
168
+ * Returns the transaction prefix hash
169
+ */
170
+ public get prefixHash(): string {
171
+ if (this.m_cached.prefix && this.m_cached.prefix === this.prefix && this.m_cached.prefixHash) {
172
+ return this.m_cached.prefixHash;
173
+ }
174
+
175
+ this.m_cached.prefix = this.prefix;
176
+
177
+ const hash = TurtleCoinCrypto.cn_fast_hash(this.m_cached.prefix);
178
+
179
+ if (this.version >= 2) {
180
+ const hash2 = TurtleCoinCrypto.cn_fast_hash(hash + TransactionVersion2Suffix);
181
+
182
+ this.m_cached.prefixHash = hash2;
183
+
184
+ return hash2;
185
+ }
186
+
187
+ this.m_cached.prefixHash = hash;
188
+
189
+ return hash;
190
+ }
191
+
192
+ /**
193
+ * Returns the transaction public key
194
+ */
195
+ public get publicKey(): string | undefined {
196
+ let result;
197
+
198
+ for (const tag of this.m_extra) {
199
+ if (tag.tag === ExtraTag.ExtraTagType.PUBKEY) {
200
+ result = (tag as ExtraTag.ExtraPublicKey).publicKey;
201
+ }
202
+ }
203
+
204
+ return result;
205
+ }
206
+
207
+ /**
208
+ * Returns the transaction size in bytes
209
+ */
210
+ public get size(): number {
211
+ return this.toBuffer().length;
212
+ }
213
+
214
+ /**
215
+ * The unlock time (or block height) for when the funds in the transaction are made available.
216
+ * Returns a BigInteger only if the value exceeds MAX_SAFE_INTEGER
217
+ */
218
+ public get unlockTime(): BigInteger.BigInteger | number {
219
+ if (this.m_unlockTime.greater(Number.MAX_SAFE_INTEGER)) {
220
+ return this.m_unlockTime;
221
+ } else {
222
+ return this.m_unlockTime.toJSNumber();
223
+ }
224
+ }
225
+
226
+ public set unlockTime(value: BigInteger.BigInteger | number) {
227
+ if (typeof value === 'number') {
228
+ value = BigInteger(value);
229
+ }
230
+
231
+ this.m_unlockTime = value;
232
+ }
233
+
234
+ /**
235
+ * Whether the transaction data is read only
236
+ * This is only set if the transaction is created from a blob as it is unlikely that
237
+ * we will be changing data after a transaction is created and signed as it would
238
+ * invalidate the transaction signatures
239
+ */
240
+ public get readonly(): boolean {
241
+ return this.m_readonly;
242
+ }
243
+
244
+ /**
245
+ * Constructs a new transaction from an existing transaction blob
246
+ * @param data the transaction data blob
247
+ * @returns the new transaction object
248
+ */
249
+ public static from(data: Buffer | string): Transaction {
250
+ const reader = new Reader(data);
251
+
252
+ const result = new Transaction();
253
+
254
+ result.m_readonly = true;
255
+
256
+ result.version = reader.varint().toJSNumber();
257
+
258
+ result.unlockTime = reader.varint();
259
+
260
+ const inputsCount = reader.varint().toJSNumber();
261
+
262
+ for (let i = 0; i < inputsCount; i++) {
263
+ const type = reader.uint8_t().toJSNumber();
264
+
265
+ switch (type) {
266
+ case TransactionInputs.InputType.COINBASE:
267
+ const blockIndex = reader.varint().toJSNumber();
268
+ result.inputs.push(new TransactionInputs.CoinbaseInput(blockIndex));
269
+ break;
270
+ case TransactionInputs.InputType.KEY:
271
+ const amount = reader.varint();
272
+ const keyOffsets: BigInteger.BigInteger[] = [];
273
+ const keyOffsetsLength = reader.varint().toJSNumber();
274
+ for (let j = 0; j < keyOffsetsLength; j++) {
275
+ keyOffsets.push(reader.varint());
276
+ }
277
+ const keyImage = reader.hash();
278
+ result.inputs.push(new TransactionInputs.KeyInput(amount, keyOffsets, keyImage));
279
+ break;
280
+ default:
281
+ throw new Error('Unknown input type');
282
+ }
283
+ }
284
+
285
+ const outputsCount = reader.varint().toJSNumber();
286
+
287
+ for (let i = 0; i < outputsCount; i++) {
288
+ const amount = reader.varint();
289
+ const type = reader.uint8_t().toJSNumber();
290
+
291
+ if (type === TransactionOutputs.OutputType.KEY) {
292
+ const key = reader.hash();
293
+ result.outputs.push(new TransactionOutputs.KeyOutput(amount, key));
294
+ } else {
295
+ throw new Error('Unknown output type');
296
+ }
297
+ }
298
+
299
+ const extraLength = reader.varint().toJSNumber();
300
+
301
+ result.m_rawExtra = reader.bytes(extraLength);
302
+
303
+ result.m_extra = readExtra(result.m_rawExtra);
304
+
305
+ if (result.publicKey) {
306
+ result.transactionKeys.publicKey = result.publicKey;
307
+ }
308
+
309
+ /* If there are bytes remaining and mod 64 then they are signatures */
310
+ if (reader.unreadBytes > 0 && reader.unreadBytes % 64 === 0) {
311
+ for (const input of result.inputs) {
312
+ if (input.type === TransactionInputs.InputType.COINBASE) {
313
+ continue;
314
+ }
315
+
316
+ const inputObject = input as TransactionInputs.KeyInput;
317
+
318
+ const signatures = [];
319
+
320
+ for (const offset of inputObject.keyOffsets) {
321
+ const sig = reader.hex(64);
322
+
323
+ if (!Common.isHex128(sig)) {
324
+ throw new Error('Invalid signature data detected');
325
+ }
326
+
327
+ signatures.push(sig);
328
+ }
329
+
330
+ result.signatures.push(signatures);
331
+ }
332
+ }
333
+
334
+ if (reader.unreadBytes > 0) {
335
+ throw new RangeError('Unstructured data found at the end of the transaction');
336
+ }
337
+
338
+ return result;
339
+ }
340
+
341
+ public version: number = 1;
342
+ public inputs: TransactionInputs.ITransactionInput[] = [];
343
+ public outputs: TransactionOutputs.ITransactionOutput[] = [];
344
+ public signatures: string[][] = [];
345
+ public ignoredField: number = 0;
346
+ public transactionKeys: ED25519.KeyPair = new ED25519.KeyPair();
347
+ protected m_unlockTime: BigInteger.BigInteger = BigInteger.zero;
348
+ protected m_rawExtra: Buffer = Buffer.alloc(0);
349
+ protected m_readonly: boolean = false;
350
+ protected m_extra: ExtraTag.IExtraTag[] = [];
351
+ protected m_cached: Cache = {prefix: '', prefixHash: '', blob: '', hash: ''};
352
+
353
+ /** @ignore */
354
+ public parseExtra(extra: Buffer) {
355
+ this.m_readonly = true;
356
+
357
+ this.m_rawExtra = extra;
358
+
359
+ this.m_extra = readExtra(this.m_rawExtra);
360
+
361
+ if (this.publicKey) {
362
+ this.transactionKeys.publicKey = this.publicKey;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Adds the arbitrary data supplied to the transaction extra field
368
+ * @param data arbitrary data to be included
369
+ */
370
+ public addData(data: Buffer) {
371
+ if (this.readonly) {
372
+ throw new Error('Transaction is read-only');
373
+ }
374
+
375
+ const subTag = new ExtraNonceTag.ExtraNonceData(data);
376
+
377
+ let found = false;
378
+ // tslint:disable-next-line:prefer-for-of
379
+ for (let i = 0; i < this.m_extra.length; i++) {
380
+ if (this.m_extra[i].tag === ExtraTag.ExtraTagType.NONCE) {
381
+ (this.m_extra[i] as ExtraTag.ExtraNonce).addTag(subTag);
382
+ found = true;
383
+ }
384
+ }
385
+
386
+ if (!found) {
387
+ const tag = new ExtraTag.ExtraNonce([subTag]);
388
+ this.m_extra.push(tag);
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Adds a merged minging tag with the supplied values to the transaction
394
+ * @param depth the depth of the blockchain branch in the merkle root
395
+ * @param merkleRoot the merkle root value
396
+ */
397
+ public addMergedMining(depth: number, merkleRoot: string) {
398
+ if (this.readonly) {
399
+ throw new Error('Transaction is read-only');
400
+ }
401
+
402
+ const tag = new ExtraTag.ExtraMergedMining(depth, merkleRoot);
403
+
404
+ this.m_extra = removeExtraTag(this.m_extra, tag.tag);
405
+
406
+ this.m_extra.push(tag);
407
+
408
+ this.m_extra.sort((a, b) => (a.tag > b.tag) ? 1 : -1);
409
+ }
410
+
411
+ /**
412
+ * Adds the supplied payment ID to the transaction extra field
413
+ * @param paymentId the payment Id to include
414
+ */
415
+ public addPaymentId(paymentId: string) {
416
+ if (this.readonly) {
417
+ throw new Error('Transaction is read-only');
418
+ }
419
+
420
+ const subTag = new ExtraNonceTag.ExtraNoncePaymentId(paymentId);
421
+
422
+ let found = false;
423
+ // tslint:disable-next-line:prefer-for-of
424
+ for (let i = 0; i < this.m_extra.length; i++) {
425
+ if (this.m_extra[i].tag === ExtraTag.ExtraTagType.NONCE) {
426
+ (this.m_extra[i] as ExtraTag.ExtraNonce).addTag(subTag);
427
+ found = true;
428
+ }
429
+ }
430
+
431
+ if (!found) {
432
+ const tag = new ExtraTag.ExtraNonce([subTag]);
433
+ this.m_extra.push(tag);
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Adds the public key for the transaction to the transaction extra field
439
+ * @param publicKey the public key of the transaction
440
+ */
441
+ public addPublicKey(publicKey: string) {
442
+ if (this.readonly) {
443
+ throw new Error('Transaction is read-only');
444
+ }
445
+
446
+ const tag = new ExtraTag.ExtraPublicKey(publicKey);
447
+
448
+ this.m_extra = removeExtraTag(this.m_extra, tag.tag);
449
+
450
+ this.m_extra.push(tag);
451
+
452
+ this.m_extra.sort((a, b) => (a.tag > b.tag) ? 1 : -1);
453
+
454
+ this.transactionKeys.publicKey = publicKey;
455
+ }
456
+
457
+ /**
458
+ * Returns a buffer representation of the transaction object
459
+ * @param [headerOnly] whether we should return just the prefix or not
460
+ * @returns the buffer representation
461
+ */
462
+ public toBuffer(headerOnly: boolean = false): Buffer {
463
+ const writer = new Writer();
464
+
465
+ writer.varint(this.version);
466
+ writer.varint(this.unlockTime);
467
+ writer.varint(this.inputs.length);
468
+
469
+ for (const input of this.inputs) {
470
+ writer.write(input.toBuffer());
471
+ }
472
+
473
+ writer.varint(this.outputs.length);
474
+
475
+ for (const output of this.outputs) {
476
+ writer.write(output.toBuffer());
477
+ }
478
+
479
+ if (this.readonly) {
480
+ writer.varint(this.m_rawExtra.length);
481
+ writer.write(this.m_rawExtra);
482
+ } else {
483
+ const extra = writeExtra(this.m_extra);
484
+ writer.varint(extra.length);
485
+ writer.write(extra);
486
+ }
487
+
488
+ if (!headerOnly && this.signatures.length !== 0) {
489
+ if (this.inputs.length !== this.signatures.length) {
490
+ throw new RangeError('Number of signatures does not match the number of inputs');
491
+ }
492
+
493
+ for (let i = 0; i < this.inputs.length; i++) {
494
+ for (const sig of this.signatures[i]) {
495
+ writer.hex(sig);
496
+ }
497
+ }
498
+ }
499
+
500
+ return writer.buffer;
501
+ }
502
+
503
+ /**
504
+ * Returns the hexadecimal (blob) representation of the transaction object
505
+ * @param [headerOnly] whether we should return just the prefix or not
506
+ * @returns the hexadecimal (blob) representation
507
+ */
508
+ public toString(headerOnly: boolean = false): string {
509
+ return this.toBuffer(headerOnly).toString('hex');
510
+ }
511
+ }
512
+
513
+ /** @ignore */
514
+ function removeExtraTag(tags: ExtraTag.IExtraTag[], removeTag: ExtraTag.ExtraTagType): ExtraTag.IExtraTag[] {
515
+ const result: ExtraTag.IExtraTag[] = [];
516
+
517
+ for (const tag of tags) {
518
+ if (tag.tag !== removeTag) {
519
+ result.push(tag);
520
+ }
521
+ }
522
+
523
+ return result;
524
+ }
525
+
526
+ /** @ignore */
527
+ function readExtra(data: Buffer): ExtraTag.IExtraTag[] {
528
+ const tags: ExtraTag.IExtraTag[] = [];
529
+ const seen = {
530
+ padding: false,
531
+ publicKey: false,
532
+ nonce: false,
533
+ mergedMining: false,
534
+ };
535
+
536
+ const reader = new Reader(data);
537
+
538
+ while (reader.unreadBytes > 0) {
539
+ let tag = 0;
540
+
541
+ try {
542
+ tag = reader.varint(true).toJSNumber();
543
+ } catch {
544
+ reader.skip();
545
+ continue;
546
+ }
547
+
548
+ let totalLength = Common.varintLength(tag);
549
+
550
+ switch (tag) {
551
+ case ExtraTag.ExtraTagType.PADDING:
552
+ if (!seen.padding) {
553
+ tags.push(ExtraTag.ExtraPadding.from(reader.bytes(totalLength)));
554
+ seen.padding = true;
555
+ } else {
556
+ reader.skip();
557
+ }
558
+ break;
559
+ case ExtraTag.ExtraTagType.PUBKEY:
560
+ totalLength += 32;
561
+ if (!seen.publicKey && reader.unreadBytes >= totalLength) {
562
+ tags.push(ExtraTag.ExtraPublicKey.from(reader.bytes(totalLength)));
563
+ seen.publicKey = true;
564
+ } else {
565
+ reader.skip();
566
+ }
567
+ break;
568
+ case ExtraTag.ExtraTagType.NONCE:
569
+ if (!seen.nonce && reader.unreadBytes >= 1) {
570
+ const tmpReader = new Reader(reader.unreadBuffer);
571
+ tmpReader.skip(totalLength);
572
+
573
+ let nonceLength = 0;
574
+ try {
575
+ nonceLength = tmpReader.varint().toJSNumber();
576
+ } catch {
577
+ reader.skip();
578
+ continue;
579
+ }
580
+
581
+ totalLength += Common.varintLength(nonceLength) + nonceLength;
582
+
583
+ tags.push(ExtraTag.ExtraNonce.from(reader.bytes(totalLength)));
584
+ seen.nonce = true;
585
+ } else {
586
+ reader.skip();
587
+ }
588
+ break;
589
+ case ExtraTag.ExtraTagType.MERGED_MINING:
590
+ if (!seen.mergedMining && reader.unreadBytes >= 1) {
591
+ const tmpReader = new Reader(reader.unreadBuffer);
592
+ tmpReader.skip(totalLength);
593
+
594
+ let mmLength = 0;
595
+ try {
596
+ mmLength = tmpReader.varint().toJSNumber();
597
+ } catch {
598
+ reader.skip();
599
+ continue;
600
+ }
601
+
602
+ totalLength += Common.varintLength(mmLength) + mmLength;
603
+
604
+ tags.push(ExtraTag.ExtraMergedMining.from(reader.bytes(totalLength)));
605
+ seen.mergedMining = true;
606
+ } else {
607
+ reader.skip();
608
+ }
609
+ break;
610
+ default:
611
+ reader.skip();
612
+ break;
613
+ }
614
+ }
615
+
616
+ return tags;
617
+ }
618
+
619
+ /** @ignore */
620
+ function writeExtra(tags: ExtraTag.IExtraTag[]): Buffer {
621
+ const writer = new Writer();
622
+
623
+ for (const tag of tags) {
624
+ writer.write(tag.toBuffer());
625
+ }
626
+
627
+ return writer.buffer;
628
+ }