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

handshake-org / hsd / 10470889206

20 Aug 2024 11:43AM UTC coverage: 69.802% (+1.3%) from 68.551%
10470889206

push

github

nodech
Merge PR #883 from 'nodech/wallet-interactive-rescan'

7723 of 12879 branches covered (59.97%)

Branch coverage included in aggregate %.

116 of 129 new or added lines in 11 files covered. (89.92%)

16 existing lines in 4 files now uncovered.

24504 of 33290 relevant lines covered (73.61%)

34258.52 hits per line

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

80.0
/lib/wallet/nodeclient.js
1
/*!
2
 * nodeclient.js - node client 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');
1✔
10
const blacklist = require('bsock/lib/blacklist');
1✔
11
const AsyncEmitter = require('bevent');
1✔
12

13
/**
14
 * Node Client
15
 * @alias module:node.NodeClient
16
 */
17

18
class NodeClient extends AsyncEmitter {
19
  /**
20
   * Create a node client.
21
   * @constructor
22
   */
23

24
  constructor(node) {
25
    super();
77✔
26

27
    this.node = node;
77✔
28
    this.network = node.network;
77✔
29
    this.filter = null;
77✔
30
    this.opened = false;
77✔
31
    this.hooks = new Map();
77✔
32

33
    this.init();
77✔
34
  }
35

36
  /**
37
   * Initialize the client.
38
   */
39

40
  init() {
41
    this.node.chain.on('connect', async (entry, block) => {
77✔
42
      if (!this.opened)
6,858✔
43
        return;
1✔
44

45
      await this.emitAsync('block connect', entry, block.txs);
6,857✔
46
    });
47

48
    this.node.chain.on('disconnect', async (entry, block) => {
77✔
49
      if (!this.opened)
67!
50
        return;
×
51

52
      await this.emitAsync('block disconnect', entry);
67✔
53
    });
54

55
    this.node.on('tx', (tx) => {
77✔
56
      if (!this.opened)
1,040✔
57
        return;
1✔
58

59
      this.emit('tx', tx);
1,039✔
60
    });
61

62
    this.node.on('reset', (tip) => {
77✔
63
      if (!this.opened)
7!
64
        return;
×
65

66
      this.emit('chain reset', tip);
7✔
67
    });
68
  }
69

70
  /**
71
   * Open the client.
72
   * @returns {Promise}
73
   */
74

75
  async open(options) {
76
    assert(!this.opened, 'NodeClient is already open.');
78✔
77
    this.opened = true;
78✔
78
    setImmediate(() => this.emit('connect'));
78✔
79
  }
80

81
  /**
82
   * Close the client.
83
   * @returns {Promise}
84
   */
85

86
  async close() {
87
    assert(this.opened, 'NodeClient is not open.');
78✔
88
    this.opened = false;
78✔
89
    setImmediate(() => this.emit('disconnect'));
78✔
90
  }
91

92
  /**
93
   * Add a listener.
94
   * @param {String} type
95
   * @param {Function} handler
96
   */
97

98
  bind(type, handler) {
99
    return this.on(type, handler);
308✔
100
  }
101

102
  /**
103
   * Add a hook.
104
   * @param {String} event
105
   * @param {Function} handler
106
   */
107

108
  hook(event, handler) {
109
    assert(typeof event === 'string', 'Event must be a string.');
231✔
110
    assert(typeof handler === 'function', 'Handler must be a function.');
231✔
111
    assert(!this.hooks.has(event), 'Hook already bound.');
231✔
112
    assert(!Object.prototype.hasOwnProperty.call(blacklist, event),
231✔
113
      'Blacklisted event.');
114
    this.hooks.set(event, handler);
231✔
115
  }
116

117
  /**
118
   * Remove a hook.
119
   * @param {String} event
120
   */
121

122
  unhook(event) {
NEW
123
    assert(typeof event === 'string', 'Event must be a string.');
×
NEW
124
    assert(!Object.prototype.hasOwnProperty.call(blacklist, event),
×
125
      'Blacklisted event.');
NEW
126
    this.hooks.delete(event);
×
127
  }
128

129
  /**
130
   * Call a hook.
131
   * @param {String} event
132
   * @param {...Object} args
133
   * @returns {Promise}
134
   */
135

136
  handleCall(event, ...args) {
137
    const hook = this.hooks.get(event);
1,999✔
138

139
    if (!hook)
1,999!
NEW
140
      throw new Error('No hook available.');
×
141

142
    return hook(...args);
1,999✔
143
  }
144

145
  /**
146
   * Get chain tip.
147
   * @returns {Promise}
148
   */
149

150
  async getTip() {
151
    return this.node.chain.tip;
×
152
  }
153

154
  /**
155
   * Get chain entry.
156
   * @param {Hash} hash
157
   * @returns {Promise}
158
   */
159

160
  async getEntry(hash) {
161
    const entry = await this.node.chain.getEntry(hash);
79✔
162

163
    if (!entry)
79✔
164
      return null;
1✔
165

166
    if (!await this.node.chain.isMainChain(entry))
78✔
167
      return null;
1✔
168

169
    return entry;
77✔
170
  }
171

172
  /**
173
   * Send a transaction. Do not wait for promise.
174
   * @param {TX} tx
175
   * @returns {Promise}
176
   */
177

178
  async send(tx) {
179
    this.node.relay(tx);
948✔
180
  }
181

182
  /**
183
   * Send a claim. Do not wait for promise.
184
   * @param {Claim} claim
185
   * @returns {Promise}
186
   */
187

188
  async sendClaim(claim) {
189
    this.node.relayClaim(claim);
8✔
190
  }
191

192
  /**
193
   * Set bloom filter.
194
   * @param {Bloom} filter
195
   * @returns {Promise}
196
   */
197

198
  async setFilter(filter) {
199
    this.filter = filter;
78✔
200
    this.node.pool.setFilter(filter);
78✔
201
  }
202

203
  /**
204
   * Add data to filter.
205
   * @param {Buffer} data
206
   * @returns {Promise}
207
   */
208

209
  async addFilter(data) {
210
    // `data` is ignored because pool.spvFilter === walletDB.filter
211
    // and therefore is already updated.
212
    // Argument is kept here to be consistent with API in
213
    // wallet/client.js (client/node.js) and wallet/nullclient.js
214
    this.node.pool.queueFilterLoad();
147,682✔
215
  }
216

217
  /**
218
   * Reset filter.
219
   * @returns {Promise}
220
   */
221

222
  async resetFilter() {
223
    this.node.pool.queueFilterLoad();
×
224
  }
225

226
  /**
227
   * Esimate smart fee.
228
   * @param {Number?} blocks
229
   * @returns {Promise}
230
   */
231

232
  async estimateFee(blocks) {
233
    if (!this.node.fees)
1,099!
234
      return this.network.feeRate;
×
235

236
    return this.node.fees.estimateFee(blocks);
1,099✔
237
  }
238

239
  /**
240
   * Get hash range.
241
   * @param {Number} start
242
   * @param {Number} end
243
   * @returns {Promise}
244
   */
245

246
  async getHashes(start = -1, end = -1) {
134✔
247
    return this.node.chain.getHashes(start, end);
67✔
248
  }
249

250
  /**
251
   * Rescan for any missed transactions.
252
   * @param {Number|Hash} start - Start block.
253
   * @returns {Promise}
254
   */
255

256
  async rescan(start) {
UNCOV
257
    if (this.node.spv)
×
UNCOV
258
      return this.node.chain.reset(start);
×
259

UNCOV
260
    return this.node.chain.scan(start, this.filter, (entry, txs) => {
×
NEW
261
      return this.handleCall('block rescan', entry, txs);
×
262
    });
263
  }
264

265
  /**
266
   * Rescan interactive for any missed transactions.
267
   * @param {Number|Hash} start - Start block.
268
   * @param {Boolean} [fullLock=false]
269
   * @returns {Promise}
270
   */
271

272
  async rescanInteractive(start, fullLock = true) {
×
273
    if (this.node.spv)
312✔
274
      return this.node.chain.reset(start);
6✔
275

276
    const iter = async (entry, txs) => {
306✔
277
      return await this.handleCall('block rescan interactive', entry, txs);
1,998✔
278
    };
279

280
    try {
306✔
281
      return await this.node.scanInteractive(
306✔
282
        start,
283
        this.filter,
284
        iter,
285
        fullLock
286
      );
287
    } catch (e) {
288
      await this.handleCall('block rescan interactive abort', e.message);
1✔
289
      throw e;
1✔
290
    }
291
  }
292

293
  /**
294
   * Get name state.
295
   * @param {Buffer} nameHash
296
   * @returns {Object}
297
   */
298

299
  async getNameStatus(nameHash) {
300
    return this.node.getNameStatus(nameHash);
413✔
301
  }
302

303
  /**
304
   * Get UTXO.
305
   * @param {Hash} hash
306
   * @param {Number} index
307
   * @returns {Object}
308
   */
309

310
  async getCoin(hash, index) {
311
    return this.node.getCoin(hash, index);
3✔
312
  }
313
}
314

315
/*
316
 * Expose
317
 */
318

319
module.exports = NodeClient;
1✔
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