• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

handshake-org / hsd / 11142522588

02 Oct 2024 10:56AM UTC coverage: 70.04% (+0.007%) from 70.033%
11142522588

push

github

nodech
Merge PR #902 from 'nodech/update-types'

7761 of 12916 branches covered (60.09%)

Branch coverage included in aggregate %.

67 of 92 new or added lines in 25 files covered. (72.83%)

8 existing lines in 5 files now uncovered.

24790 of 33559 relevant lines covered (73.87%)

34019.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.12
/lib/primitives/coin.js
1
/*!
2
 * coin.js - coin object for hsd
3
 * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
4
 * https://github.com/handshake-org/hsd
5
 */
6

7
'use strict';
8

9
const assert = require('bsert');
70✔
10
const Amount = require('../ui/amount');
70✔
11
const Output = require('./output');
70✔
12
const Network = require('../protocol/network');
70✔
13
const consensus = require('../protocol/consensus');
70✔
14
const Outpoint = require('./outpoint');
70✔
15
const util = require('../utils/util');
70✔
16

17
/** @typedef {import('bufio').BufferReader} BufferReader */
18
/** @typedef {import('../types').BufioWriter} BufioWriter */
19
/** @typedef {import('../types').NetworkType} NetworkType */
20
/** @typedef {import('../types').HexHash} HexHash */
21
/** @typedef {import('./tx')} TX */
22

23
/**
24
 * Coin
25
 * Represents an unspent output.
26
 * @alias module:primitives.Coin
27
 * @extends Output
28
 * @property {Number} version
29
 * @property {Number} height
30
 * @property {Amount} value
31
 * @property {Script} script
32
 * @property {Boolean} coinbase
33
 * @property {Hash} hash
34
 * @property {Number} index
35
 */
36

37
class Coin extends Output {
38
  /**
39
   * Create a coin.
40
   * @constructor
41
   * @param {Object?} [options]
42
   */
43

44
  constructor(options) {
45
    super();
362,890✔
46

47
    this.version = 1;
362,890✔
48
    this.height = -1;
362,890✔
49
    this.coinbase = false;
362,890✔
50
    this.hash = consensus.ZERO_HASH;
362,890✔
51
    this.index = 0;
362,890✔
52

53
    if (options)
362,890✔
54
      this.fromOptions(options);
67✔
55
  }
56

57
  /**
58
   * Inject options into coin.
59
   * @param {Object} [options]
60
   */
61

62
  fromOptions(options) {
63
    assert(options, 'Coin data is required.');
87✔
64

65
    if (options.version != null) {
87✔
66
      assert((options.version >>> 0) === options.version,
40✔
67
        'Version must be a uint32.');
68
      this.version = options.version;
40✔
69
    }
70

71
    if (options.height != null) {
87✔
72
      if (options.height !== -1) {
44✔
73
        assert((options.height >>> 0) === options.height,
24✔
74
          'Height must be a uint32.');
75
        this.height = options.height;
24✔
76
      } else {
77
        this.height = -1;
20✔
78
      }
79
    }
80

81
    if (options.value != null) {
87!
82
      assert(Number.isSafeInteger(options.value) && options.value >= 0,
87✔
83
        'Value must be a uint64.');
84
      this.value = options.value;
87✔
85
    }
86

87
    if (options.address)
87!
88
      this.address.fromOptions(options.address);
87✔
89

90
    if (options.covenant)
87!
91
      this.covenant.fromOptions(options.covenant);
×
92

93
    if (options.coinbase != null) {
87✔
94
      assert(typeof options.coinbase === 'boolean',
40✔
95
        'Coinbase must be a boolean.');
96
      this.coinbase = options.coinbase;
40✔
97
    }
98

99
    if (options.hash != null) {
87✔
100
      assert(Buffer.isBuffer(options.hash));
67✔
101
      this.hash = options.hash;
67✔
102
    }
103

104
    if (options.index != null) {
87✔
105
      assert((options.index >>> 0) === options.index,
67✔
106
        'Index must be a uint32.');
107
      this.index = options.index;
67✔
108
    }
109

110
    return this;
87✔
111
  }
112

113
  /**
114
   * Clone the coin.
115
   * @returns {this}
116
   */
117

118
  clone() {
119
    assert(false, 'Coins are not cloneable.');
×
NEW
120
    return this;
×
121
  }
122

123
  /**
124
   * Calculate number of confirmations since coin was created.
125
   * @param {Number} height - Current chain height. Network
126
   * height is used if not passed in.
127
   * @return {Number}
128
   */
129

130
  getDepth(height) {
131
    assert(typeof height === 'number', 'Must pass a height.');
1✔
132

133
    if (this.height === -1)
1!
134
      return 0;
×
135

136
    if (height === -1)
1!
137
      return 0;
×
138

139
    if (height < this.height)
1!
140
      return 0;
×
141

142
    return height - this.height + 1;
1✔
143
  }
144

145
  /**
146
   * Serialize coin to a key
147
   * suitable for a hash table.
148
   * @returns {Buffer}
149
   */
150

151
  toKey() {
152
    return Outpoint.toKey(this.hash, this.index);
210,360✔
153
  }
154

155
  /**
156
   * Inject properties from hash table key.
157
   * @param {Buffer} key
158
   * @returns {Coin}
159
   */
160

161
  fromKey(key) {
162
    const {hash, index} = Outpoint.fromKey(key);
×
163
    this.hash = hash;
×
164
    this.index = index;
×
165
    return this;
×
166
  }
167

168
  /**
169
   * Instantiate coin from hash table key.
170
   * @param {Buffer} key
171
   * @returns {Coin}
172
   */
173

174
  static fromKey(key) {
175
    return new this().fromKey(key);
×
176
  }
177

178
  /**
179
   * Get little-endian hash.
180
   * @returns {HexHash?}
181
   */
182

183
  txid() {
184
    if (!this.hash)
33!
185
      return null;
×
186
    return this.hash.toString('hex');
33✔
187
  }
188

189
  /**
190
   * Convert the coin to a more user-friendly object.
191
   * @returns {Object}
192
   */
193

194
  format() {
195
    return {
1✔
196
      version: this.version,
197
      height: this.height,
198
      value: Amount.coin(this.value),
199
      address: this.address,
200
      covenant: this.covenant.toJSON(),
201
      coinbase: this.coinbase,
202
      hash: this.txid(),
203
      index: this.index
204
    };
205
  }
206

207
  /**
208
   * Convert the coin to an object suitable
209
   * for JSON serialization.
210
   * @param {Network} [network]
211
   * @param {Boolean} [minimal]
212
   * @returns {Object}
213
   */
214

215
  getJSON(network, minimal) {
216
    network = Network.get(network);
92✔
217

218
    return {
92✔
219
      version: this.version,
220
      height: this.height,
221
      value: this.value,
222
      address: this.address.toString(network),
223
      covenant: this.covenant.toJSON(),
224
      coinbase: this.coinbase,
225
      hash: !minimal ? this.txid() : undefined,
92✔
226
      index: !minimal ? this.index : undefined
92✔
227
    };
228
  }
229

230
  /**
231
   * Inject JSON properties into coin.
232
   * @param {Object} json
233
   * @param {(NetworkType|Network)?} [network]
234
   */
235

236
  fromJSON(json, network) {
237
    assert(json, 'Coin data required.');
36✔
238
    assert((json.version >>> 0) === json.version, 'Version must be a uint32.');
36✔
239
    assert(json.height === -1 || (json.height >>> 0) === json.height,
36✔
240
      'Height must be a uint32.');
241
    assert(util.isU64(json.value), 'Value must be a uint64.');
36✔
242
    assert(typeof json.coinbase === 'boolean', 'Coinbase must be a boolean.');
36✔
243

244
    this.version = json.version;
36✔
245
    this.height = json.height;
36✔
246
    this.value = json.value;
36✔
247
    this.address.fromString(json.address, network);
36✔
248
    this.coinbase = json.coinbase;
36✔
249

250
    if (json.covenant != null)
36!
251
      this.covenant.fromJSON(json.covenant);
36✔
252

253
    if (json.hash != null) {
36✔
254
      assert((json.index >>> 0) === json.index, 'Index must be a uint32.');
3✔
255
      this.hash = util.parseHex(json.hash, 32);
3✔
256
      this.index = json.index;
3✔
257
    }
258

259
    return this;
36✔
260
  }
261

262
  /**
263
   * Calculate size of coin.
264
   * @returns {Number}
265
   */
266

267
  getSize() {
268
    return 17 + this.address.getSize() + this.covenant.getVarSize();
57,022✔
269
  }
270

271
  /**
272
   * Write the coin to a buffer writer.
273
   * @param {BufioWriter} bw
274
   * @returns {BufioWriter}
275
   */
276

277
  write(bw) {
278
    let height = this.height;
57,022✔
279

280
    if (height === -1)
57,022✔
281
      height = 0xffffffff;
14,951✔
282

283
    bw.writeU32(this.version);
57,022✔
284
    bw.writeU32(height);
57,022✔
285
    bw.writeU64(this.value);
57,022✔
286
    this.address.write(bw);
57,022✔
287
    this.covenant.write(bw);
57,022✔
288
    bw.writeU8(this.coinbase ? 1 : 0);
57,022✔
289

290
    return bw;
57,022✔
291
  }
292

293
  /**
294
   * Inject properties from serialized buffer writer.
295
   * @param {BufferReader} br
296
   */
297

298
  read(br) {
299
    this.version = br.readU32();
284,476✔
300
    this.height = br.readU32();
284,476✔
301
    this.value = br.readU64();
284,476✔
302
    this.address.read(br);
284,476✔
303
    this.covenant.read(br);
284,476✔
304
    this.coinbase = br.readU8() === 1;
284,476✔
305

306
    if (this.height === 0xffffffff)
284,476✔
307
      this.height = -1;
24,986✔
308

309
    return this;
284,476✔
310
  }
311

312
  /**
313
   * Inject properties from TX.
314
   * @param {TX} tx
315
   * @param {Number} index
316
   * @param {Number} height
317
   */
318

319
  fromTX(tx, index, height) {
320
    assert(typeof index === 'number');
77,113✔
321
    assert(typeof height === 'number');
77,113✔
322
    assert(index >= 0 && index < tx.outputs.length);
77,113✔
323
    this.version = tx.version;
77,113✔
324
    this.height = height;
77,113✔
325
    this.value = tx.outputs[index].value;
77,113✔
326
    this.address = tx.outputs[index].address;
77,113✔
327
    this.covenant = tx.outputs[index].covenant;
77,113✔
328
    this.coinbase = tx.isCoinbase();
77,113✔
329
    this.hash = tx.hash();
77,113✔
330
    this.index = index;
77,113✔
331
    return this;
77,113✔
332
  }
333

334
  /**
335
   * Instantiate a coin from a TX
336
   * @param {TX} tx
337
   * @param {Number} index - Output index.
338
   * @param {Number} height - Chain height.
339
   * @returns {Coin}
340
   */
341

342
  static fromTX(tx, index, height) {
343
    return new this().fromTX(tx, index, height);
56,068✔
344
  }
345

346
  /**
347
   * Test an object to see if it is a Coin.
348
   * @param {Object} obj
349
   * @returns {Boolean}
350
   */
351

352
  static isCoin(obj) {
353
    return obj instanceof Coin;
×
354
  }
355
}
356

357
/*
358
 * Expose
359
 */
360

361
module.exports = Coin;
70✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc