@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.
- package/.github/workflows/ci.yml +27 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/turtlecoin-utils.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.travis.yml +11 -0
- package/CONTRIBUTING.md +3 -0
- package/LICENSE +674 -0
- package/README.md +203 -0
- package/config.json +7 -0
- package/docs/.nojekyll +0 -0
- package/docs/CNAME +1 -0
- package/docs/assets/css/main.css +2321 -0
- package/docs/assets/images/icons.png +0 -0
- package/docs/assets/images/icons@2x.png +0 -0
- package/docs/assets/images/widgets.png +0 -0
- package/docs/assets/images/widgets@2x.png +0 -0
- package/docs/assets/js/main.js +1 -0
- package/docs/assets/js/search.js +3 -0
- package/docs/classes/address.html +964 -0
- package/docs/classes/addressprefix.html +431 -0
- package/docs/classes/block.html +965 -0
- package/docs/classes/blocktemplate.html +695 -0
- package/docs/classes/cryptonote.html +1137 -0
- package/docs/classes/ed25519.keypair.html +400 -0
- package/docs/classes/ed25519.keys.html +373 -0
- package/docs/classes/extranoncetag.extranoncedata.html +454 -0
- package/docs/classes/extranoncetag.extranoncepaymentid.html +453 -0
- package/docs/classes/extranoncetag.iextranonce.html +347 -0
- package/docs/classes/extratag.extramergedmining.html +494 -0
- package/docs/classes/extratag.extranonce.html +530 -0
- package/docs/classes/extratag.extrapadding.html +456 -0
- package/docs/classes/extratag.extrapublickey.html +460 -0
- package/docs/classes/extratag.iextratag.html +355 -0
- package/docs/classes/levinpacket.html +674 -0
- package/docs/classes/levinpayloads.handshake.html +731 -0
- package/docs/classes/levinpayloads.ilevinpayload.html +318 -0
- package/docs/classes/levinpayloads.liteblock.html +494 -0
- package/docs/classes/levinpayloads.missingtransactions.html +494 -0
- package/docs/classes/levinpayloads.newblock.html +540 -0
- package/docs/classes/levinpayloads.newtransactions.html +402 -0
- package/docs/classes/levinpayloads.peerentry.html +610 -0
- package/docs/classes/levinpayloads.ping.html +450 -0
- package/docs/classes/levinpayloads.rawblock.html +344 -0
- package/docs/classes/levinpayloads.requestchain.html +402 -0
- package/docs/classes/levinpayloads.requestgetobjects.html +448 -0
- package/docs/classes/levinpayloads.requesttxpool.html +402 -0
- package/docs/classes/levinpayloads.responsechain.html +494 -0
- package/docs/classes/levinpayloads.responsegetobjects.html +540 -0
- package/docs/classes/levinpayloads.timedsync.html +540 -0
- package/docs/classes/multisig.html +930 -0
- package/docs/classes/multisigmessage.html +694 -0
- package/docs/classes/parentblock.html +347 -0
- package/docs/classes/transaction.html +925 -0
- package/docs/classes/transactioninputs.coinbaseinput.html +390 -0
- package/docs/classes/transactioninputs.itransactioninput.html +321 -0
- package/docs/classes/transactioninputs.keyinput.html +459 -0
- package/docs/classes/transactionoutputs.itransactionoutput.html +317 -0
- package/docs/classes/transactionoutputs.keyoutput.html +422 -0
- package/docs/enums/extranoncetag.noncetagtype.html +246 -0
- package/docs/enums/extratag.extratagtype.html +280 -0
- package/docs/enums/levinprotocol.commandtype.html +391 -0
- package/docs/enums/transactioninputs.inputtype.html +246 -0
- package/docs/enums/transactionoutputs.outputtype.html +229 -0
- package/docs/globals.html +238 -0
- package/docs/index.html +271 -0
- package/docs/interfaces/interfaces.config.html +590 -0
- package/docs/interfaces/interfaces.daemonblocktemplateresponse.html +323 -0
- package/docs/interfaces/interfaces.generatedinput.html +304 -0
- package/docs/interfaces/interfaces.generatedoutput.html +285 -0
- package/docs/interfaces/interfaces.inputkeys.html +304 -0
- package/docs/interfaces/interfaces.ipreparedtransaction.html +268 -0
- package/docs/interfaces/interfaces.output.html +399 -0
- package/docs/interfaces/interfaces.preparedringsignature.html +377 -0
- package/docs/interfaces/interfaces.preparedtransaction.html +329 -0
- package/docs/interfaces/interfaces.randomoutput.html +285 -0
- package/docs/interfaces/interfaces.transactionrecipient.html +285 -0
- package/docs/interfaces/multisiginterfaces.partialkeyimage.html +277 -0
- package/docs/interfaces/multisiginterfaces.partialsigningkey.html +277 -0
- package/docs/modules/ed25519.html +195 -0
- package/docs/modules/extranoncetag.html +208 -0
- package/docs/modules/extratag.html +216 -0
- package/docs/modules/interfaces.html +231 -0
- package/docs/modules/levinpayloads.html +247 -0
- package/docs/modules/levinprotocol.html +191 -0
- package/docs/modules/multisiginterfaces.html +195 -0
- package/docs/modules/transactioninputs.html +208 -0
- package/docs/modules/transactionoutputs.html +204 -0
- package/index.d.ts +417 -0
- package/index.js +1508 -0
- package/lib/base58.js +220 -0
- package/lib/biginteger.js +1591 -0
- package/lib/blocktemplate.js +408 -0
- package/lib/crypto.js +19698 -0
- package/lib/mnemonic.js +1204 -0
- package/lib/nacl-fast-cn.js +608 -0
- package/lib/ringsigs.js +24262 -0
- package/lib/sha3.js +477 -0
- package/package.json +58 -0
- package/src/Address.ts +433 -0
- package/src/AddressPrefix.ts +117 -0
- package/src/Block.ts +556 -0
- package/src/BlockTemplate.ts +289 -0
- package/src/Common.ts +105 -0
- package/src/Config.ts +66 -0
- package/src/CryptoNote.ts +1072 -0
- package/src/LevinPacket.ts +366 -0
- package/src/Multisig.ts +600 -0
- package/src/MultisigMessage.ts +374 -0
- package/src/ParentBlock.ts +39 -0
- package/src/Transaction.ts +628 -0
- package/src/Types/ED25519.ts +187 -0
- package/src/Types/IExtraNonce.ts +225 -0
- package/src/Types/IExtraTag.ts +507 -0
- package/src/Types/ITransaction.ts +230 -0
- package/src/Types/ITransactionInput.ts +190 -0
- package/src/Types/ITransactionOutput.ts +108 -0
- package/src/Types/LevinPayloads.ts +1576 -0
- package/src/Types/MultisigInterfaces.ts +65 -0
- package/src/Types/PortableStorage.ts +289 -0
- package/src/Types.ts +36 -0
- package/src/index.ts +36 -0
- package/test/template.json +6 -0
- package/test/test.js +1457 -0
- package/tests/blocktemplate.json +6 -0
- package/tests/tests.js +215 -0
- package/tsconfig.json +15 -0
- package/tslint.json +36 -0
- package/typedoc.json +10 -0
- 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
|
+
}
|