• 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

59.87
/lib/net/pool.js
1
/*!
2
 * pool.js - peer management 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 EventEmitter = require('events');
1✔
11
const {Lock} = require('bmutex');
1✔
12
const IP = require('binet');
1✔
13
const tcp = require('btcp');
1✔
14
const UPNP = require('bupnp');
1✔
15
const socks = require('bsocks');
1✔
16
const List = require('blst');
1✔
17
const base32 = require('bcrypto/lib/encoding/base32');
1✔
18
const {BufferMap, BufferSet} = require('buffer-map');
1✔
19
const blake2b = require('bcrypto/lib/blake2b');
1✔
20
const {BloomFilter, RollingFilter} = require('@handshake-org/bfilter');
1✔
21
const rng = require('bcrypto/lib/random');
1✔
22
const secp256k1 = require('bcrypto/lib/secp256k1');
1✔
23
const {siphash} = require('bcrypto/lib/siphash');
1✔
24
const {lookup} = require('./lookup');
1✔
25
const util = require('../utils/util');
1✔
26
const common = require('./common');
1✔
27
const chainCommon = require('../blockchain/common');
1✔
28
const Address = require('../primitives/address');
1✔
29
const BIP152 = require('./bip152');
1✔
30
const Network = require('../protocol/network');
1✔
31
const Peer = require('./peer');
1✔
32
const HostList = require('./hostlist');
1✔
33
const InvItem = require('../primitives/invitem');
1✔
34
const packets = require('./packets');
1✔
35
const consensus = require('../protocol/consensus');
1✔
36
const NameState = require('../covenants/namestate');
1✔
37
const services = common.services;
1✔
38
const invTypes = InvItem.types;
1✔
39
const packetTypes = packets.types;
1✔
40
const scores = HostList.scores;
1✔
41

42
/**
43
 * Pool
44
 * A pool of peers for handling all network activity.
45
 * @alias module:net.Pool
46
 * @extends EventEmitter
47
 */
48

49
class Pool extends EventEmitter {
50
  /**
51
   * Create a pool.
52
   * @constructor
53
   * @param {Object} options
54
   */
55

56
  constructor(options) {
57
    super();
178✔
58

59
    this.opened = false;
178✔
60
    this.options = new PoolOptions(options);
178✔
61

62
    this.network = this.options.network;
176✔
63
    this.logger = this.options.logger.context('net');
176✔
64
    this.chain = this.options.chain;
176✔
65
    this.mempool = this.options.mempool;
176✔
66
    this.server = this.options.createServer();
176✔
67
    this.brontide = this.options.createServer();
176✔
68
    this.nonces = this.options.nonces;
176✔
69

70
    this.locker = new Lock(true, BufferMap);
176✔
71
    this.connected = false;
176✔
72
    this.disconnecting = false;
176✔
73
    this.syncing = false;
176✔
74
    this.discovering = false;
176✔
75
    this.spvFilter = null;
176✔
76
    this.txFilter = null;
176✔
77
    this.blockMap = new BufferSet();
176✔
78
    this.txMap = new BufferSet();
176✔
79
    this.claimMap = new BufferSet();
176✔
80
    this.airdropMap = new BufferSet();
176✔
81
    this.compactBlocks = new BufferSet();
176✔
82
    this.invMap = new BufferMap();
176✔
83
    this.nameMap = new BufferMap();
176✔
84
    this.pendingFilter = null;
176✔
85
    this.refillTimer = null;
176✔
86
    this.discoverTimer = null;
176✔
87
    this.connectedGroups = new BufferSet();
176✔
88

89
    this.checkpoints = false;
176✔
90
    this.headerChain = new List();
176✔
91
    this.headerNext = null;
176✔
92
    this.headerTip = null;
176✔
93

94
    this.peers = new PeerList();
176✔
95
    this.hosts = new HostList(this.options);
176✔
96
    this.id = 0;
176✔
97

98
    if (this.options.spv) {
176✔
99
      this.spvFilter = BloomFilter.fromRate(
15✔
100
        20000, 0.001, BloomFilter.flags.ALL);
101
    }
102

103
    if (!this.options.mempool)
176✔
104
      this.txFilter = new RollingFilter(50000, 0.000001);
17✔
105

106
    this.init();
176✔
107
  }
108

109
  /**
110
   * Initialize the pool.
111
   * @private
112
   */
113

114
  init() {
115
    this.server.on('error', (err) => {
176✔
116
      this.emit('error', err);
×
117
    });
118

119
    this.server.on('connection', (socket) => {
176✔
120
      try {
45✔
121
        this.handleSocket(socket, false);
45✔
122
      } catch (e) {
123
        this.emit('error', e);
×
124
        return;
×
125
      }
126
      this.emit('connection', socket);
45✔
127
    });
128

129
    this.server.on('listening', () => {
176✔
130
      const data = this.server.address();
52✔
131
      this.logger.info(
52✔
132
        'Pool server listening on %s (port=%d).',
133
        data.address, data.port);
134
      this.emit('listening', data);
52✔
135
    });
136

137
    this.brontide.on('error', (err) => {
176✔
138
      this.emit('error', err);
×
139
    });
140

141
    this.brontide.on('connection', (socket) => {
176✔
142
      try {
×
143
        this.handleSocket(socket, true);
×
144
      } catch (e) {
145
        this.emit('error', e);
×
146
        return;
×
147
      }
148
      this.emit('connection', socket);
×
149
    });
150

151
    this.brontide.on('listening', () => {
176✔
152
      const data = this.brontide.address();
52✔
153
      this.logger.info(
52✔
154
        'Brontide server listening on %s (port=%d).',
155
        data.address, data.port);
156
      this.emit('listening', data);
52✔
157
    });
158

159
    this.chain.on('block', (block, entry) => {
176✔
160
      this.emit('block', block, entry);
8,881✔
161
    });
162

163
    this.chain.on('reset', () => {
176✔
164
      try {
11✔
165
        if (this.checkpoints)
11!
166
          this.resetChain();
×
167
        this.forceSync();
11✔
168
      } catch (e) {
169
        this.emit('error', e);
1✔
170
      }
171
    });
172

173
    this.chain.on('full', () => {
176✔
174
      try {
172✔
175
        this.sync();
172✔
176
      } catch (e) {
177
        this.emit('error', e);
×
178
        return;
×
179
      }
180
      this.emit('full');
172✔
181
      this.logger.info('Chain is fully synced (height=%d).', this.chain.height);
172✔
182
    });
183

184
    this.chain.on('bad orphan', (err, id) => {
176✔
185
      try {
×
186
        this.handleBadOrphan(packets.types.BLOCK, err, id);
×
187
      } catch (e) {
188
        this.emit('error', e);
×
189
      }
190
    });
191

192
    if (this.mempool) {
176✔
193
      this.mempool.on('tx', (tx) => {
159✔
194
        this.emit('tx', tx);
1,103✔
195
      });
196

197
      this.mempool.on('claim', (claim) => {
159✔
198
        this.emit('claim', claim);
18✔
199
      });
200

201
      this.mempool.on('airdrop', (proof) => {
159✔
202
        this.emit('airdrop', proof);
3✔
203
      });
204

205
      this.mempool.on('bad orphan', (err, id) => {
159✔
206
        try {
×
207
          this.handleBadOrphan(packets.types.TX, err, id);
×
208
        } catch (e) {
209
          this.emit('error', e);
×
210
        }
211
      });
212
    }
213

214
    if (!this.options.spv) {
176✔
215
      if (this.mempool) {
161✔
216
        this.mempool.on('tx', (tx) => {
159✔
217
          try {
1,103✔
218
            this.announceTX(tx);
1,103✔
219
          } catch (e) {
220
            this.emit('error', e);
×
221
          }
222
        });
223

224
        this.mempool.on('claim', (claim) => {
159✔
225
          try {
18✔
226
            this.announceClaim(claim);
18✔
227
          } catch (e) {
228
            this.emit('error', e);
×
229
          }
230
        });
231

232
        this.mempool.on('airdrop', (proof) => {
159✔
233
          try {
3✔
234
            this.announceAirdrop(proof);
3✔
235
          } catch (e) {
236
            this.emit('error', e);
×
237
          }
238
        });
239
      }
240

241
      // Normally we would also broadcast
242
      // competing chains, but we want to
243
      // avoid getting banned if an evil
244
      // miner sends us an invalid competing
245
      // chain that we can't connect and
246
      // verify yet.
247
      this.chain.on('block', (block) => {
161✔
248
        if (!this.chain.synced)
8,329!
249
          return;
×
250

251
        try {
8,329✔
252
          this.announceBlock(block);
8,329✔
253
        } catch (e) {
254
          this.emit('error', e);
×
255
        }
256
      });
257
    }
258
  }
259

260
  /**
261
   * Open the pool, wait for the chain to load.
262
   * @returns {Promise}
263
   */
264

265
  async open() {
266
    assert(!this.opened, 'Pool is already open.');
173✔
267
    this.opened = true;
173✔
268

269
    this.logger.info('Pool loaded (maxpeers=%d).', this.options.maxOutbound);
173✔
270

271
    this.logger.info('Pool identity key: %s.',
173✔
272
      base32.encode(this.hosts.brontide.key));
273

274
    this.resetChain();
173✔
275
  }
276

277
  /**
278
   * Close and destroy the pool.
279
   * @method
280
   * @alias Pool#close
281
   * @returns {Promise}
282
   */
283

284
  async close() {
285
    assert(this.opened, 'Pool is not open.');
173✔
286
    this.opened = false;
173✔
287
    return this.disconnect();
173✔
288
  }
289

290
  /**
291
   * Reset header chain.
292
   */
293

294
  resetChain() {
295
    if (!this.options.checkpoints)
184!
296
      return;
×
297

298
    this.checkpoints = false;
184✔
299
    this.headerTip = null;
184✔
300
    this.headerChain.reset();
184✔
301
    this.headerNext = null;
184✔
302

303
    const tip = this.chain.tip;
184✔
304

305
    if (tip.height < this.network.lastCheckpoint) {
184✔
306
      this.checkpoints = true;
22✔
307
      this.headerTip = this.getNextTip(tip.height);
22✔
308
      this.headerChain.push(new HeaderEntry(tip.hash, tip.height));
22✔
309
      this.logger.info(
22✔
310
        'Initialized header chain to height %d (checkpoint=%x).',
311
        tip.height, this.headerTip.hash);
312
    }
313
  }
314

315
  /**
316
   * Connect to the network.
317
   * @method
318
   * @returns {Promise}
319
   */
320

321
  async connect() {
322
    const unlock = await this.locker.lock();
111✔
323
    try {
111✔
324
      return await this._connect();
111✔
325
    } finally {
326
      unlock();
111✔
327
    }
328
  }
329

330
  /**
331
   * Connect to the network (no lock).
332
   * @method
333
   * @returns {Promise}
334
   */
335

336
  async _connect() {
337
    assert(this.opened, 'Pool is not opened.');
111✔
338

339
    if (this.connected)
111✔
340
      return;
1✔
341

342
    await this.hosts.open();
110✔
343

344
    await this.discoverGateway();
110✔
345
    await this.discoverSeeds();
110✔
346

347
    await this.listen();
110✔
348

349
    this.fillOutbound();
110✔
350

351
    this.startTimer();
110✔
352

353
    this.connected = true;
110✔
354
  }
355

356
  /**
357
   * Disconnect from the network.
358
   * @method
359
   * @returns {Promise}
360
   */
361

362
  async disconnect() {
363
    const unlock = await this.locker.lock();
173✔
364
    try {
173✔
365
      return await this._disconnect();
173✔
366
    } finally {
367
      unlock();
173✔
368
    }
369
  }
370

371
  /**
372
   * Disconnect from the network.
373
   * @method
374
   * @returns {Promise}
375
   */
376

377
  async _disconnect() {
378
    for (const item of this.invMap.values())
173✔
379
      item.resolve();
2✔
380

381
    if (!this.connected)
173✔
382
      return;
63✔
383

384
    this.disconnecting = true;
110✔
385

386
    this.peers.destroy();
110✔
387

388
    this.blockMap.clear();
110✔
389
    this.txMap.clear();
110✔
390
    this.claimMap.clear();
110✔
391
    this.airdropMap.clear();
110✔
392

393
    if (this.pendingFilter != null) {
110✔
394
      clearTimeout(this.pendingFilter);
5✔
395
      this.pendingFilter = null;
5✔
396
    }
397

398
    this.checkpoints = false;
110✔
399
    this.headerTip = null;
110✔
400
    this.headerChain.reset();
110✔
401
    this.headerNext = null;
110✔
402

403
    this.stopTimer();
110✔
404

405
    await this.hosts.close();
110✔
406

407
    await this.unlisten();
110✔
408

409
    this.disconnecting = false;
110✔
410
    this.syncing = false;
110✔
411
    this.connected = false;
110✔
412
  }
413

414
  /**
415
   * Start listening on a server socket.
416
   * @method
417
   * @private
418
   * @returns {Promise}
419
   */
420

421
  async listen() {
422
    assert(this.server);
110✔
423
    assert(this.brontide);
110✔
424
    assert(!this.connected, 'Already listening.');
110✔
425

426
    if (!this.options.listen)
110✔
427
      return;
58✔
428

429
    this.server.maxConnections = this.options.maxInbound;
52✔
430
    this.brontide.maxConnections = this.options.maxInbound;
52✔
431

432
    await this.server.listen(this.options.port, this.options.host);
52✔
433
    await this.brontide.listen(this.options.brontidePort, this.options.host);
52✔
434
  }
435

436
  /**
437
   * Stop listening on server socket.
438
   * @method
439
   * @private
440
   * @returns {Promise}
441
   */
442

443
  async unlisten() {
444
    assert(this.server);
110✔
445
    assert(this.brontide);
110✔
446
    assert(this.connected, 'Not listening.');
110✔
447

448
    if (!this.options.listen)
110✔
449
      return;
58✔
450

451
    await this.server.close();
52✔
452
    await this.brontide.close();
52✔
453
  }
454

455
  /**
456
   * Start discovery timer.
457
   * @private
458
   */
459

460
  startTimer() {
461
    assert(this.refillTimer == null, 'Refill timer already started.');
110✔
462
    assert(this.discoverTimer == null, 'Discover timer already started.');
110✔
463

464
    this.refillTimer = setInterval(() => this.refill(), Pool.REFILL_INTERVAL);
110✔
465

466
    this.discoverTimer =
110✔
467
      setInterval(() => this.discover(), Pool.DISCOVERY_INTERVAL);
×
468
  }
469

470
  /**
471
   * Stop discovery timer.
472
   * @private
473
   */
474

475
  stopTimer() {
476
    assert(this.refillTimer != null, 'Refill timer already stopped.');
110✔
477
    assert(this.discoverTimer != null, 'Discover timer already stopped.');
110✔
478

479
    clearInterval(this.refillTimer);
110✔
480
    this.refillTimer = null;
110✔
481

482
    clearInterval(this.discoverTimer);
110✔
483
    this.discoverTimer = null;
110✔
484
  }
485

486
  /**
487
   * Rediscover seeds and internet gateway.
488
   * Attempt to add port mapping once again.
489
   * @returns {Promise}
490
   */
491

492
  async discover() {
493
    if (this.discovering)
×
494
      return;
×
495

496
    try {
×
497
      this.discovering = true;
×
498
      await this.discoverGateway();
×
499
      await this.discoverSeeds(true);
×
500
    } finally {
501
      this.discovering = false;
×
502
    }
503
  }
504

505
  /**
506
   * Attempt to add port mapping (i.e.
507
   * remote:8333->local:8333) via UPNP.
508
   * @returns {Promise}
509
   */
510

511
  async discoverGateway() {
512
    const src = this.options.publicPort;
110✔
513
    const dest = this.options.port;
110✔
514

515
    // Pointless if we're not listening.
516
    if (!this.options.listen)
110✔
517
      return false;
58✔
518

519
    // UPNP is always optional, since
520
    // it's likely to not work anyway.
521
    if (!this.options.upnp)
52!
522
      return false;
52✔
523

524
    let wan;
525
    try {
×
526
      this.logger.debug('Discovering internet gateway (upnp).');
×
527
      wan = await UPNP.discover();
×
528
    } catch (e) {
529
      this.logger.debug('Could not discover internet gateway (upnp).');
×
530
      this.logger.debug(e);
×
531
      return false;
×
532
    }
533

534
    let host;
535
    try {
×
536
      host = await wan.getExternalIP();
×
537
    } catch (e) {
538
      this.logger.debug('Could not find external IP (upnp).');
×
539
      this.logger.debug(e);
×
540
      return false;
×
541
    }
542

543
    this.logger.debug(
×
544
      'Adding port mapping %d->%d.',
545
      src, dest);
546

547
    try {
×
548
      await wan.addPortMapping(host, src, dest);
×
549
    } catch (e) {
550
      this.logger.debug('Could not add port mapping (upnp).');
×
551
      this.logger.debug(e);
×
552
      return false;
×
553
    }
554

555
    if (this.hosts.addLocal(host, src, scores.UPNP))
×
556
      this.logger.info('External IP found (upnp): %s.', host);
×
557

558
    return true;
×
559
  }
560

561
  /**
562
   * Attempt to resolve DNS seeds if necessary.
563
   * @param {Boolean} checkPeers
564
   * @returns {Promise}
565
   */
566

567
  async discoverSeeds(checkPeers) {
568
    if (this.hosts.dnsSeeds.length === 0)
110✔
569
      return;
99✔
570

571
    const max = Math.min(2, this.options.maxOutbound);
11✔
572
    const size = this.hosts.size();
11✔
573

574
    let total = 0;
11✔
575
    for (let peer = this.peers.head(); peer; peer = peer.next) {
11✔
576
      if (!peer.outbound)
×
577
        continue;
×
578

579
      if (peer.connected) {
×
580
        if (++total > max)
×
581
          break;
×
582
      }
583
    }
584

585
    if (size === 0 || (checkPeers && total < max)) {
11!
586
      this.logger.warning('Could not find enough peers.');
×
587
      this.logger.warning('Hitting DNS seeds...');
×
588

589
      await this.hosts.discoverSeeds();
×
590

591
      this.logger.info(
×
592
        'Resolved %d hosts from DNS seeds.',
593
        this.hosts.size() - size);
594
    }
595
  }
596

597
  /**
598
   * Handle incoming connection.
599
   * @private
600
   * @param {net.Socket} socket
601
   */
602

603
  handleSocket(socket, encrypted) {
604
    if (!socket.remoteAddress) {
45!
605
      this.logger.debug('Ignoring disconnected peer.');
×
606
      socket.destroy();
×
607
      return;
×
608
    }
609

610
    const ip = IP.normalize(socket.remoteAddress);
45✔
611

612
    if (this.peers.inbound >= this.options.maxInbound) {
45!
613
      this.logger.debug('Ignoring peer: too many inbound (%s).', ip);
×
614
      socket.destroy();
×
615
      return;
×
616
    }
617

618
    if (this.hosts.isBanned(ip)) {
45!
619
      this.logger.debug('Ignoring banned peer (%s).', ip);
×
620
      socket.destroy();
×
621
      return;
×
622
    }
623

624
    const host = IP.toHostname(ip, socket.remotePort);
45✔
625

626
    assert(!this.peers.map.has(host), 'Port collision.');
45✔
627

628
    this.addInbound(socket, encrypted);
45✔
629
  }
630

631
  /**
632
   * Add a loader peer. Necessary for
633
   * a sync to even begin.
634
   * @private
635
   */
636

637
  addLoader() {
638
    if (!this.opened)
112!
639
      return;
×
640

641
    assert(!this.peers.load);
112✔
642

643
    for (let peer = this.peers.head(); peer; peer = peer.next) {
112✔
644
      if (!peer.outbound)
1!
645
        continue;
1✔
646

647
      this.logger.info(
×
648
        'Repurposing peer for loader (%s).',
649
        peer.hostname());
650

651
      this.setLoader(peer);
×
652

653
      return;
×
654
    }
655

656
    const addr = this.getHost();
112✔
657

658
    if (!addr)
112✔
659
      return;
60✔
660

661
    const peer = this.createOutbound(addr);
52✔
662

663
    this.logger.info('Adding loader peer (%s).', peer.hostname());
52✔
664

665
    this.peers.add(peer);
52✔
666
    this.connectedGroups.add(addr.getGroup());
52✔
667

668
    this.setLoader(peer);
52✔
669
  }
670

671
  /**
672
   * Add a loader peer. Necessary for
673
   * a sync to even begin.
674
   * @private
675
   */
676

677
  setLoader(peer) {
678
    if (!this.opened)
52!
679
      return;
×
680

681
    assert(peer.outbound);
52✔
682
    assert(!this.peers.load);
52✔
683
    assert(!peer.loader);
52✔
684

685
    peer.loader = true;
52✔
686
    this.peers.load = peer;
52✔
687

688
    this.sendSync(peer);
52✔
689

690
    this.emit('loader', peer);
52✔
691
  }
692

693
  /**
694
   * Start the blockchain sync.
695
   */
696

697
  startSync() {
698
    if (!this.opened)
96!
699
      return;
×
700

701
    assert(this.connected, 'Pool is not connected!');
96✔
702

703
    this.syncing = true;
96✔
704
    this.resync(false);
96✔
705
  }
706

707
  /**
708
   * Force sending of a sync to each peer.
709
   */
710

711
  forceSync() {
712
    if (!this.opened)
11!
713
      return;
×
714

715
    assert(this.connected, 'Pool is not connected!');
11✔
716

717
    this.resync(true);
10✔
718
  }
719

720
  /**
721
   * Send a sync to each peer.
722
   */
723

724
  sync(force) {
725
    this.resync(false);
172✔
726
  }
727

728
  /**
729
   * Stop the sync.
730
   * @private
731
   */
732

733
  stopSync() {
734
    if (!this.syncing)
×
735
      return;
×
736

737
    this.syncing = false;
×
738

739
    for (let peer = this.peers.head(); peer; peer = peer.next) {
×
740
      if (!peer.outbound)
×
741
        continue;
×
742

743
      if (!peer.syncing)
×
744
        continue;
×
745

746
      peer.syncing = false;
×
747
      peer.merkleBlock = null;
×
748
      peer.merkleTime = -1;
×
749
      peer.merkleMatches = 0;
×
750
      peer.merkleMap = null;
×
751
      peer.blockTime = -1;
×
752
      peer.blockMap.clear();
×
753
      peer.compactBlocks.clear();
×
754
    }
755

756
    this.blockMap.clear();
×
757
    this.compactBlocks.clear();
×
758
  }
759

760
  /**
761
   * Send a sync to each peer.
762
   * @private
763
   * @param {Boolean?} force
764
   * @returns {Promise}
765
   */
766

767
  async resync(force) {
768
    if (!this.syncing)
278✔
769
      return;
162✔
770

771
    let locator;
772
    try {
116✔
773
      locator = await this.chain.getLocator();
116✔
774
    } catch (e) {
775
      this.emit('error', e);
×
776
      return;
×
777
    }
778

779
    for (let peer = this.peers.head(); peer; peer = peer.next) {
116✔
780
      if (!peer.outbound)
82!
781
        continue;
×
782

783
      if (!force && peer.syncing)
82✔
784
        continue;
34✔
785

786
      if (force) {
48✔
787
        peer.lastTip = consensus.ZERO_HASH;
10✔
788
        peer.lastStop = consensus.ZERO_HASH;
10✔
789
      }
790

791
      this.sendLocator(locator, peer);
48✔
792
    }
793
  }
794

795
  /**
796
   * Test whether a peer is sync-worthy.
797
   * @param {Peer} peer
798
   * @returns {Boolean}
799
   */
800

801
  isSyncable(peer) {
802
    if (!this.syncing)
181✔
803
      return false;
58✔
804

805
    if (peer.destroyed)
123✔
806
      return false;
1✔
807

808
    if (!peer.handshake)
122✔
809
      return false;
38✔
810

811
    if (!(peer.services & services.NETWORK))
84!
812
      return false;
×
813

814
    if (!peer.loader) {
84!
815
      if (!this.chain.synced)
×
816
        return false;
×
817
    }
818

819
    return true;
84✔
820
  }
821

822
  /**
823
   * Start syncing from peer.
824
   * @method
825
   * @param {Peer} peer
826
   * @returns {Promise}
827
   */
828

829
  async sendSync(peer) {
830
    if (peer.syncing)
97!
831
      return false;
×
832

833
    if (!this.isSyncable(peer))
97✔
834
      return false;
58✔
835

836
    peer.syncing = true;
39✔
837
    peer.blockTime = Date.now();
39✔
838

839
    let locator;
840
    try {
39✔
841
      locator = await this.chain.getLocator();
39✔
842
    } catch (e) {
843
      peer.syncing = false;
3✔
844
      peer.blockTime = -1;
3✔
845
      this.emit('error', e);
3✔
846
      return false;
3✔
847
    }
848

849
    return this.sendLocator(locator, peer);
36✔
850
  }
851

852
  /**
853
   * Send a chain locator and start syncing from peer.
854
   * @method
855
   * @param {Hash[]} locator
856
   * @param {Peer} peer
857
   * @returns {Boolean}
858
   */
859

860
  sendLocator(locator, peer) {
861
    if (!this.isSyncable(peer))
84✔
862
      return false;
39✔
863

864
    // Ask for the mempool if we're synced.
865
    if (this.network.requestMempool) {
45!
866
      if (peer.loader && this.chain.synced)
45!
867
        peer.sendMempool();
45✔
868
    }
869

870
    peer.syncing = true;
45✔
871
    peer.blockTime = Date.now();
45✔
872

873
    if (this.checkpoints) {
45!
874
      peer.sendGetHeaders(locator, this.headerTip.hash);
×
875
      return true;
×
876
    }
877

878
    peer.sendGetBlocks(locator, consensus.ZERO_HASH);
45✔
879

880
    return true;
45✔
881
  }
882

883
  /**
884
   * Send `mempool` to all peers.
885
   */
886

887
  sendMempool() {
888
    for (let peer = this.peers.head(); peer; peer = peer.next)
×
889
      peer.sendMempool();
×
890
  }
891

892
  /**
893
   * Send `getaddr` to all peers.
894
   */
895

896
  sendGetAddr() {
897
    for (let peer = this.peers.head(); peer; peer = peer.next)
×
898
      peer.sendGetAddr();
×
899
  }
900

901
  /**
902
   * Request current header chain blocks.
903
   * @private
904
   * @param {Peer} peer
905
   */
906

907
  resolveHeaders(peer) {
908
    const items = [];
×
909

910
    for (let node = this.headerNext; node; node = node.next) {
×
911
      this.headerNext = node.next;
×
912

913
      items.push(node.hash);
×
914

915
      if (items.length === common.MAX_INV)
×
916
        break;
×
917
    }
918

919
    this.getBlock(peer, items);
×
920
  }
921

922
  /**
923
   * Update all peer heights by their best hash.
924
   * @param {Hash} hash
925
   * @param {Number} height
926
   */
927

928
  resolveHeight(hash, height) {
929
    let total = 0;
876✔
930

931
    for (let peer = this.peers.head(); peer; peer = peer.next) {
876✔
932
      if (!peer.bestHash.equals(hash))
909✔
933
        continue;
34✔
934

935
      if (peer.bestHeight !== height) {
875!
936
        peer.bestHeight = height;
×
937
        total += 1;
×
938
      }
939
    }
940

941
    if (total > 0)
876!
942
      this.logger.debug('Resolved height for %d peers.', total);
×
943
  }
944

945
  /**
946
   * Find the next checkpoint.
947
   * @private
948
   * @param {Number} height
949
   * @returns {Object}
950
   */
951

952
  getNextTip(height) {
953
    for (const next of this.network.checkpoints) {
22✔
954
      if (next.height > height)
22!
955
        return new HeaderEntry(next.hash, next.height);
22✔
956
    }
957

958
    throw new Error('Next checkpoint not found.');
×
959
  }
960

961
  /**
962
   * Announce broadcast list to peer.
963
   * @param {Peer} peer
964
   */
965

966
  announceList(peer) {
967
    const blocks = [];
90✔
968
    const txs = [];
90✔
969
    const claims = [];
90✔
970
    const proofs = [];
90✔
971

972
    for (const item of this.invMap.values()) {
90✔
973
      switch (item.type) {
×
974
        case invTypes.BLOCK:
975
          blocks.push(item.msg);
×
976
          break;
×
977
        case invTypes.TX:
978
          txs.push(item.msg);
×
979
          break;
×
980
        case invTypes.CLAIM:
981
          claims.push(item.msg);
×
982
          break;
×
983
        case invTypes.AIRDROP:
984
          proofs.push(item.msg);
×
985
          break;
×
986
        default:
987
          assert(false, 'Bad item type.');
×
988
          break;
×
989
      }
990
    }
991

992
    if (blocks.length > 0)
90!
993
      peer.announceBlock(blocks);
×
994

995
    if (txs.length > 0)
90!
996
      peer.announceTX(txs);
×
997

998
    if (claims.length > 0)
90!
999
      peer.announceClaim(claims);
×
1000

1001
    if (proofs.length > 0)
90!
1002
      peer.announceAirdrop(proofs);
×
1003
  }
1004

1005
  /**
1006
   * Get a block/tx from the broadcast map.
1007
   * @private
1008
   * @param {Peer} peer
1009
   * @param {InvItem} item
1010
   * @returns {Promise}
1011
   */
1012

1013
  getBroadcasted(peer, item) {
1014
    let name = '';
976✔
1015
    let type = 0;
976✔
1016

1017
    if (item.isTX()) {
976✔
1018
      name = 'tx';
45✔
1019
      type = invTypes.TX;
45✔
1020
    } else if (item.isBlock()) {
931!
1021
      name = 'block';
931✔
1022
      type = invTypes.BLOCK;
931✔
1023
    } else if (item.isClaim()) {
×
1024
      name = 'claim';
×
1025
      type = invTypes.CLAIM;
×
1026
    } else if (item.isAirdrop()) {
×
1027
      name = 'airdrop';
×
1028
      type = invTypes.AIRDROP;
×
1029
    }
1030

1031
    const entry = this.invMap.get(item.hash);
976✔
1032

1033
    if (!entry)
976!
1034
      return null;
976✔
1035

1036
    if (type !== entry.type) {
×
1037
      this.logger.debug(
×
1038
        'Peer requested item with the wrong type (%s).',
1039
        peer.hostname());
1040
      return null;
×
1041
    }
1042

1043
    this.logger.debug(
×
1044
      'Peer requested %s %x (%s).',
1045
      name,
1046
      item.hash,
1047
      peer.hostname());
1048

1049
    entry.handleAck(peer);
×
1050

1051
    return entry.msg;
×
1052
  }
1053

1054
  /**
1055
   * Get a block/tx either from the broadcast map, mempool, or blockchain.
1056
   * @method
1057
   * @private
1058
   * @param {Peer} peer
1059
   * @param {InvItem} item
1060
   * @returns {Promise}
1061
   */
1062

1063
  async getItem(peer, item) {
1064
    const entry = this.getBroadcasted(peer, item);
883✔
1065

1066
    if (entry)
883!
1067
      return entry;
×
1068

1069
    if (item.isTX()) {
883✔
1070
      if (!this.mempool)
45!
1071
        return null;
×
1072
      return this.mempool.getTX(item.hash);
45✔
1073
    }
1074

1075
    if (item.isClaim()) {
838!
1076
      if (!this.mempool)
×
1077
        return null;
×
1078
      return this.mempool.getClaim(item.hash);
×
1079
    }
1080

1081
    if (item.isAirdrop()) {
838!
1082
      if (!this.mempool)
×
1083
        return null;
×
1084
      return this.mempool.getAirdrop(item.hash);
×
1085
    }
1086

1087
    if (this.chain.options.spv)
838!
1088
      return null;
×
1089

1090
    if (this.chain.options.prune)
838!
1091
      return null;
×
1092

1093
    return this.chain.getBlock(item.hash);
838✔
1094
  }
1095

1096
  /**
1097
   * Send a block from the broadcast list or chain.
1098
   * @method
1099
   * @private
1100
   * @param {Peer} peer
1101
   * @param {InvItem} item
1102
   * @returns {Boolean}
1103
   */
1104

1105
  async sendBlock(peer, item) {
1106
    const broadcasted = this.getBroadcasted(peer, item);
93✔
1107

1108
    // Check for a broadcasted item first.
1109
    if (broadcasted) {
93!
1110
      peer.send(new packets.BlockPacket(broadcasted));
×
1111
      return true;
×
1112
    }
1113

1114
    if (this.chain.options.spv
93!
1115
        || this.chain.options.prune) {
1116
      return false;
×
1117
    }
1118

1119
    const block = await this.chain.getRawBlock(item.hash);
93✔
1120

1121
    peer.sendRaw(packetTypes.BLOCK, block);
93✔
1122

1123
    return true;
92✔
1124
  }
1125

1126
  /**
1127
   * Create an outbound peer with no special purpose.
1128
   * @private
1129
   * @param {NetAddress} addr
1130
   * @returns {Peer}
1131
   */
1132

1133
  createOutbound(addr) {
1134
    const peer = Peer.fromOutbound(this.options, addr);
75✔
1135

1136
    this.hosts.markAttempt(addr.hostname);
75✔
1137

1138
    this.bindPeer(peer);
75✔
1139

1140
    this.logger.debug('Connecting to %s.', peer.hostname());
75✔
1141

1142
    peer.tryOpen();
75✔
1143

1144
    return peer;
75✔
1145
  }
1146

1147
  /**
1148
   * Accept an inbound socket.
1149
   * @private
1150
   * @param {net.Socket} socket
1151
   * @returns {Peer}
1152
   */
1153

1154
  createInbound(socket, encrypted) {
1155
    const peer = Peer.fromInbound(this.options, socket, encrypted);
45✔
1156

1157
    this.bindPeer(peer);
45✔
1158

1159
    peer.tryOpen();
45✔
1160

1161
    return peer;
45✔
1162
  }
1163

1164
  /**
1165
   * Allocate new peer id.
1166
   * @returns {Number}
1167
   */
1168

1169
  uid() {
1170
    const MAX = Number.MAX_SAFE_INTEGER;
120✔
1171

1172
    if (this.id >= MAX - this.peers.size() - 1)
120!
1173
      this.id = 0;
×
1174

1175
    // Once we overflow, there's a chance
1176
    // of collisions. Unlikely to happen
1177
    // unless we have tried to connect 9
1178
    // quadrillion times, but still
1179
    // account for it.
1180
    do {
120✔
1181
      this.id += 1;
120✔
1182
    } while (this.peers.find(this.id));
1183

1184
    return this.id;
120✔
1185
  }
1186

1187
  /**
1188
   * Bind to peer events.
1189
   * @private
1190
   * @param {Peer} peer
1191
   */
1192

1193
  bindPeer(peer) {
1194
    peer.id = this.uid();
120✔
1195

1196
    peer.onPacket = (packet) => {
120✔
1197
      return this.handlePacket(peer, packet);
2,718✔
1198
    };
1199

1200
    peer.on('error', (err) => {
120✔
1201
      this.logger.debug(err);
40✔
1202
    });
1203

1204
    peer.once('connect', async () => {
120✔
1205
      try {
45✔
1206
        await this.handleConnect(peer);
45✔
1207
      } catch (e) {
1208
        this.emit('error', e);
×
1209
      }
1210
    });
1211

1212
    peer.once('open', async () => {
120✔
1213
      try {
90✔
1214
        await this.handleOpen(peer);
90✔
1215
      } catch (e) {
1216
        this.emit('error', e);
×
1217
      }
1218
    });
1219

1220
    peer.once('close', async (connected) => {
120✔
1221
      try {
120✔
1222
        await this.handleClose(peer, connected);
120✔
1223
      } catch (e) {
1224
        this.emit('error', e);
×
1225
      }
1226
    });
1227

1228
    peer.once('ban', async () => {
120✔
1229
      try {
1✔
1230
        await this.handleBan(peer);
1✔
1231
      } catch (e) {
1232
        this.emit('error', e);
×
1233
      }
1234
    });
1235
  }
1236

1237
  /**
1238
   * Handle peer packet event.
1239
   * @method
1240
   * @private
1241
   * @param {Peer} peer
1242
   * @param {Packet} packet
1243
   * @returns {Promise}
1244
   */
1245

1246
  async handlePacket(peer, packet) {
1247
    switch (packet.type) {
2,718!
1248
      case packetTypes.VERSION:
1249
        await this.handleVersion(peer, packet);
90✔
1250
        break;
90✔
1251
      case packetTypes.VERACK:
1252
        await this.handleVerack(peer, packet);
90✔
1253
        break;
90✔
1254
      case packetTypes.PING:
1255
        await this.handlePing(peer, packet);
×
1256
        break;
×
1257
      case packetTypes.PONG:
1258
        await this.handlePong(peer, packet);
×
1259
        break;
×
1260
      case packetTypes.GETADDR:
1261
        await this.handleGetAddr(peer, packet);
45✔
1262
        break;
45✔
1263
      case packetTypes.ADDR:
1264
        await this.handleAddr(peer, packet);
4✔
1265
        break;
4✔
1266
      case packetTypes.INV:
1267
        await this.handleInv(peer, packet);
596✔
1268
        break;
596✔
1269
      case packetTypes.GETDATA:
1270
        await this.handleGetData(peer, packet);
613✔
1271
        break;
612✔
1272
      case packetTypes.NOTFOUND:
1273
        await this.handleNotFound(peer, packet);
25✔
1274
        break;
25✔
1275
      case packetTypes.GETBLOCKS:
1276
        await this.handleGetBlocks(peer, packet);
56✔
1277
        break;
56✔
1278
      case packetTypes.GETHEADERS:
1279
        await this.handleGetHeaders(peer, packet);
×
1280
        break;
×
1281
      case packetTypes.HEADERS:
1282
        await this.handleHeaders(peer, packet);
×
1283
        break;
×
1284
      case packetTypes.SENDHEADERS:
1285
        await this.handleSendHeaders(peer, packet);
×
1286
        break;
×
1287
      case packetTypes.BLOCK:
1288
        await this.handleBlock(peer, packet);
92✔
1289
        break;
92✔
1290
      case packetTypes.TX:
1291
        await this.handleTX(peer, packet);
9✔
1292
        break;
9✔
1293
      case packetTypes.REJECT:
1294
        await this.handleReject(peer, packet);
2✔
1295
        break;
2✔
1296
      case packetTypes.MEMPOOL:
1297
        await this.handleMempool(peer, packet);
39✔
1298
        break;
39✔
1299
      case packetTypes.FILTERLOAD:
1300
        await this.handleFilterLoad(peer, packet);
10✔
1301
        break;
10✔
1302
      case packetTypes.FILTERADD:
1303
        await this.handleFilterAdd(peer, packet);
×
1304
        break;
×
1305
      case packetTypes.FILTERCLEAR:
1306
        await this.handleFilterClear(peer, packet);
×
1307
        break;
×
1308
      case packetTypes.MERKLEBLOCK:
1309
        await this.handleMerkleBlock(peer, packet);
456✔
1310
        break;
456✔
1311
      case packetTypes.FEEFILTER:
1312
        await this.handleFeeFilter(peer, packet);
×
1313
        break;
×
1314
      case packetTypes.SENDCMPCT:
1315
        await this.handleSendCmpct(peer, packet);
77✔
1316
        break;
77✔
1317
      case packetTypes.CMPCTBLOCK:
1318
        await this.handleCmpctBlock(peer, packet);
332✔
1319
        break;
332✔
1320
      case packetTypes.GETBLOCKTXN:
1321
        await this.handleGetBlockTxn(peer, packet);
38✔
1322
        break;
38✔
1323
      case packetTypes.BLOCKTXN:
1324
        await this.handleBlockTxn(peer, packet);
38✔
1325
        break;
38✔
1326
      case packetTypes.GETPROOF:
1327
        await this.handleGetProof(peer, packet);
54✔
1328
        break;
53✔
1329
      case packetTypes.PROOF:
1330
        await this.handleProof(peer, packet);
52✔
1331
        break;
52✔
1332
      case packetTypes.CLAIM:
1333
        await this.handleClaim(peer, packet);
×
1334
        break;
×
1335
      case packetTypes.AIRDROP:
1336
        await this.handleAirdrop(peer, packet);
×
1337
        break;
×
1338
      case packetTypes.UNKNOWN:
1339
        await this.handleUnknown(peer, packet);
×
1340
        break;
×
1341
      default:
1342
        assert(false, 'Bad packet type.');
×
1343
        break;
×
1344
    }
1345

1346
    this.emit('packet', packet, peer);
2,716✔
1347
  }
1348

1349
  /**
1350
   * Handle peer connect event.
1351
   * @method
1352
   * @private
1353
   * @param {Peer} peer
1354
   */
1355

1356
  async handleConnect(peer) {
1357
    this.logger.info('Connected to %s.', peer.hostname());
45✔
1358

1359
    if (peer.outbound)
45!
1360
      this.hosts.markSuccess(peer.hostname());
45✔
1361

1362
    this.emit('peer connect', peer);
45✔
1363
  }
1364

1365
  /**
1366
   * Handle peer open event.
1367
   * @method
1368
   * @private
1369
   * @param {Peer} peer
1370
   */
1371

1372
  async handleOpen(peer) {
1373
    // Advertise our address.
1374
    if (peer.outbound) {
90✔
1375
      if (this.options.listen) {
45✔
1376
        const addr = this.hosts.getLocal(peer.address);
33✔
1377

1378
        if (addr)
33✔
1379
          peer.send(new packets.AddrPacket([addr]));
2✔
1380
      }
1381

1382
      // Find some more peers.
1383
      if (peer.version >= 3) {
45!
1384
        peer.sendGetAddr();
45✔
1385
        peer.gettingAddr = true;
45✔
1386
      }
1387
    }
1388

1389
    // We want compact blocks!
1390
    if (this.options.compact)
90✔
1391
      peer.sendCompact(this.options.blockMode);
81✔
1392

1393
    // Relay our spv filter if we have one.
1394
    if (this.spvFilter)
90✔
1395
      peer.sendFilterLoad(this.spvFilter);
9✔
1396

1397
    // Announce our currently broadcasted items.
1398
    this.announceList(peer);
90✔
1399

1400
    // Set a fee rate filter.
1401
    if (this.options.feeRate !== -1)
90!
1402
      peer.sendFeeRate(this.options.feeRate);
×
1403

1404
    if (peer.outbound) {
90✔
1405
      // Start syncing the chain.
1406
      this.sendSync(peer);
45✔
1407

1408
      // Mark success.
1409
      this.hosts.markAck(peer.hostname(), peer.services);
45✔
1410

1411
      // If we don't have an ack'd
1412
      // loader yet consider it dead.
1413
      if (!peer.loader) {
45!
1414
        if (this.peers.load && !this.peers.load.handshake) {
×
1415
          assert(this.peers.load.loader);
×
1416
          this.peers.load.loader = false;
×
1417
          this.peers.load = null;
×
1418
        }
1419
      }
1420

1421
      // If we do not have a loader,
1422
      // use this peer.
1423
      if (!this.peers.load)
45!
1424
        this.setLoader(peer);
×
1425
    }
1426

1427
    this.emit('peer open', peer);
90✔
1428
  }
1429

1430
  /**
1431
   * Handle peer close event.
1432
   * @method
1433
   * @private
1434
   * @param {Peer} peer
1435
   * @param {Boolean} connected
1436
   */
1437

1438
  async handleClose(peer, connected) {
1439
    const loader = peer.loader;
120✔
1440
    const size = peer.blockMap.size;
120✔
1441

1442
    this.removePeer(peer);
120✔
1443

1444
    if (loader) {
120✔
1445
      this.logger.info('Removed loader peer (%s).', peer.hostname());
52✔
1446
      if (this.checkpoints)
52✔
1447
        this.resetChain();
11✔
1448
    }
1449

1450
    this.nonces.remove(peer.hostname());
120✔
1451

1452
    this.emit('peer close', peer, connected);
120✔
1453

1454
    if (!this.opened)
120✔
1455
      return;
80✔
1456

1457
    if (this.disconnecting)
40!
1458
      return;
×
1459

1460
    if (this.chain.synced && size > 0) {
40!
1461
      this.logger.warning('Peer disconnected with requested blocks.');
×
1462
      this.logger.warning('Resending sync...');
×
1463
      this.forceSync();
×
1464
    }
1465
  }
1466

1467
  /**
1468
   * Handle ban event.
1469
   * @method
1470
   * @private
1471
   * @param {Peer} peer
1472
   */
1473

1474
  async handleBan(peer) {
1475
    this.ban(peer.address);
1✔
1476
    this.emit('ban', peer);
1✔
1477
  }
1478

1479
  /**
1480
   * Handle peer version event.
1481
   * @method
1482
   * @private
1483
   * @param {Peer} peer
1484
   * @param {VersionPacket} packet
1485
   */
1486

1487
  async handleVersion(peer, packet) {
1488
    this.logger.info(
90✔
1489
      'Received version (%s): version=%d height=%d services=%s agent=%s',
1490
      peer.hostname(),
1491
      packet.version,
1492
      packet.height,
1493
      packet.services.toString(2),
1494
      packet.agent);
1495

1496
    this.network.time.add(peer.hostname(), packet.time);
90✔
1497
    this.nonces.remove(peer.hostname());
90✔
1498

1499
    if (!peer.outbound && packet.remote.isRoutable())
90!
1500
      this.hosts.markLocal(packet.remote);
×
1501
  }
1502

1503
  /**
1504
   * Handle `verack` packet.
1505
   * @method
1506
   * @private
1507
   * @param {Peer} peer
1508
   * @param {VerackPacket} packet
1509
   */
1510

1511
  async handleVerack(peer, packet) {
1512
    ;
1513
  }
1514

1515
  /**
1516
   * Handle `ping` packet.
1517
   * @method
1518
   * @private
1519
   * @param {Peer} peer
1520
   * @param {PingPacket} packet
1521
   */
1522

1523
  async handlePing(peer, packet) {
1524
    ;
1525
  }
1526

1527
  /**
1528
   * Handle `pong` packet.
1529
   * @method
1530
   * @private
1531
   * @param {Peer} peer
1532
   * @param {PongPacket} packet
1533
   */
1534

1535
  async handlePong(peer, packet) {
1536
    ;
1537
  }
1538

1539
  /**
1540
   * Handle `getaddr` packet.
1541
   * @method
1542
   * @private
1543
   * @param {Peer} peer
1544
   * @param {GetAddrPacket} packet
1545
   */
1546

1547
  async handleGetAddr(peer, packet) {
1548
    if (peer.outbound) {
45!
1549
      this.logger.debug(
×
1550
        'Ignoring getaddr from outbound node (%s).',
1551
        peer.hostname());
1552
      return;
×
1553
    }
1554

1555
    if (peer.sentAddr) {
45!
1556
      this.logger.debug(
×
1557
        'Ignoring repeated getaddr (%s).',
1558
        peer.hostname());
1559
      return;
×
1560
    }
1561

1562
    peer.sentAddr = true;
45✔
1563

1564
    const addrs = this.hosts.toArray();
45✔
1565
    const items = [];
45✔
1566

1567
    for (const addr of addrs) {
45✔
1568
      if (addr.hasKey())
4!
1569
        continue;
×
1570

1571
      if (!peer.addrFilter.added(addr.hostname, 'ascii'))
4✔
1572
        continue;
2✔
1573

1574
      items.push(addr);
2✔
1575
    }
1576

1577
    if (items.length === 0)
45✔
1578
      return;
43✔
1579

1580
    this.logger.debug(
2✔
1581
      'Sending %d addrs to peer (%s)',
1582
      items.length,
1583
      peer.hostname());
1584

1585
    for (let i = 0; i < 1000; i += 1000) {
2✔
1586
      const out = items.slice(i, i + 1000);
2✔
1587
      peer.send(new packets.AddrPacket(out));
2✔
1588
    }
1589
  }
1590

1591
  /**
1592
   * Handle peer addr event.
1593
   * @method
1594
   * @private
1595
   * @param {Peer} peer
1596
   * @param {AddrPacket} packet
1597
   */
1598

1599
  async handleAddr(peer, packet) {
1600
    const addrs = packet.items;
4✔
1601
    const now = this.network.now();
4✔
1602
    const since = now - 10 * 60;
4✔
1603
    const services = this.options.getRequiredServices();
4✔
1604
    const relay = [];
4✔
1605

1606
    if (addrs.length > 1000) {
4!
1607
      peer.increaseBan(100);
×
1608
      return;
×
1609
    }
1610

1611
    if (peer.version < 3)
4!
1612
      return;
×
1613

1614
    for (const addr of addrs) {
4✔
1615
      peer.addrFilter.add(addr.hostname, 'ascii');
4✔
1616

1617
      if (!addr.isRoutable())
4✔
1618
        continue;
2✔
1619

1620
      if (!addr.hasServices(services))
2!
1621
        continue;
×
1622

1623
      if (addr.port === 0)
2!
1624
        continue;
×
1625

1626
      if (addr.hasKey())
2!
1627
        continue;
×
1628

1629
      if (this.hosts.isBanned(addr.host))
2!
1630
        continue;
×
1631

1632
      if (addr.time <= 100000000 || addr.time > now + 10 * 60)
2!
1633
        addr.time = now - 5 * 24 * 60 * 60;
×
1634

1635
      if (!peer.gettingAddr && addrs.length < 10) {
2!
1636
        if (addr.time > since)
2!
1637
          relay.push(addr);
2✔
1638
      }
1639

1640
      this.hosts.add(addr, peer.address);
2✔
1641
    }
1642

1643
    if (addrs.length < 1000)
4!
1644
      peer.gettingAddr = false;
4✔
1645

1646
    this.logger.info(
4✔
1647
      'Received %d addrs (hosts=%d, peers=%d) (%s).',
1648
      addrs.length,
1649
      this.hosts.size(),
1650
      this.peers.size(),
1651
      peer.hostname());
1652

1653
    if (relay.length > 0) {
4✔
1654
      const peers = [];
2✔
1655

1656
      this.logger.debug('Relaying %d addrs to random peers.', relay.length);
2✔
1657

1658
      for (let peer = this.peers.head(); peer; peer = peer.next) {
2✔
1659
        if (peer.handshake)
4✔
1660
          peers.push(peer);
2✔
1661
      }
1662

1663
      if (peers.length > 0) {
2!
1664
        for (const addr of relay) {
2✔
1665
          const [hi, lo] = siphash(addr.raw, this.hosts.key);
2✔
1666
          const peer1 = peers[(hi >>> 0) % peers.length];
2✔
1667
          const peer2 = peers[(lo >>> 0) % peers.length];
2✔
1668
          const key = Buffer.from(addr.hostname, 'binary');
2✔
1669
          const msg = new packets.AddrPacket([addr]);
2✔
1670

1671
          if (peer1.addrFilter.added(key))
2!
1672
            peer1.send(msg);
×
1673

1674
          if (peer2.addrFilter.added(key))
2!
1675
            peer2.send(msg);
×
1676
        }
1677
      }
1678
    }
1679
  }
1680

1681
  /**
1682
   * Handle `inv` packet.
1683
   * @method
1684
   * @private
1685
   * @param {Peer} peer
1686
   * @param {InvPacket} packet
1687
   */
1688

1689
  async handleInv(peer, packet) {
1690
    const unlock = await this.locker.lock();
596✔
1691
    try {
596✔
1692
      return await this._handleInv(peer, packet);
596✔
1693
    } finally {
1694
      unlock();
596✔
1695
    }
1696
  }
1697

1698
  /**
1699
   * Handle `inv` packet (without a lock).
1700
   * @method
1701
   * @private
1702
   * @param {Peer} peer
1703
   * @param {InvPacket} packet
1704
   */
1705

1706
  async _handleInv(peer, packet) {
1707
    const items = packet.items;
596✔
1708

1709
    if (items.length > common.MAX_INV) {
596!
1710
      peer.increaseBan(100);
×
1711
      return;
×
1712
    }
1713

1714
    const blocks = [];
596✔
1715
    const txs = [];
596✔
1716
    const claims = [];
596✔
1717
    const proofs = [];
596✔
1718

1719
    let unknown = -1;
596✔
1720

1721
    for (const item of items) {
596✔
1722
      switch (item.type) {
975!
1723
        case invTypes.BLOCK:
1724
          blocks.push(item.hash);
930✔
1725
          break;
930✔
1726
        case invTypes.TX:
1727
          txs.push(item.hash);
45✔
1728
          break;
45✔
1729
        case invTypes.CLAIM:
1730
          claims.push(item.hash);
×
1731
          break;
×
1732
        case invTypes.AIRDROP:
1733
          proofs.push(item.hash);
×
1734
          break;
×
1735
        default:
1736
          unknown = item.type;
×
1737
          continue;
×
1738
      }
1739
      peer.invFilter.add(item.hash);
975✔
1740
    }
1741

1742
    this.logger.spam(
596✔
1743
      'Received inv packet with %d items: blocks=%d txs=%d claims=%d (%s).',
1744
      items.length, blocks.length, txs.length, claims.length, peer.hostname());
1745

1746
    if (unknown !== -1) {
596!
1747
      this.logger.warning(
×
1748
        'Peer sent an unknown inv type: %d (%s).',
1749
        unknown, peer.hostname());
1750
    }
1751

1752
    if (blocks.length > 0)
596✔
1753
      await this.handleBlockInv(peer, blocks);
595✔
1754

1755
    if (txs.length > 0)
596✔
1756
      await this.handleTXInv(peer, txs);
29✔
1757

1758
    if (claims.length > 0)
596!
1759
      await this.handleClaimInv(peer, claims);
×
1760

1761
    if (proofs.length > 0)
596!
1762
      await this.handleAirdropInv(peer, proofs);
×
1763
  }
1764

1765
  /**
1766
   * Handle `inv` packet from peer (containing only BLOCK types).
1767
   * @method
1768
   * @private
1769
   * @param {Peer} peer
1770
   * @param {Hash[]} hashes
1771
   * @returns {Promise}
1772
   */
1773

1774
  async handleBlockInv(peer, hashes) {
1775
    assert(hashes.length > 0);
595✔
1776

1777
    if (!this.syncing)
595!
1778
      return;
×
1779

1780
    // Always keep track of the peer's best hash.
1781
    if (!peer.loader || this.chain.synced) {
595!
1782
      const hash = hashes[hashes.length - 1];
595✔
1783
      peer.bestHash = hash;
595✔
1784
    }
1785

1786
    // Ignore for now if we're still syncing
1787
    if (!this.chain.synced && !peer.loader)
595!
1788
      return;
×
1789

1790
    // Request headers instead.
1791
    if (this.checkpoints)
595!
1792
      return;
×
1793

1794
    this.logger.debug(
595✔
1795
      'Received %d block hashes from peer (%s).',
1796
      hashes.length,
1797
      peer.hostname());
1798

1799
    const items = [];
595✔
1800

1801
    let exists = null;
595✔
1802

1803
    for (let i = 0; i < hashes.length; i++) {
595✔
1804
      const hash = hashes[i];
930✔
1805

1806
      // Resolve orphan chain.
1807
      if (this.chain.hasOrphan(hash)) {
930✔
1808
        this.logger.debug('Received known orphan hash (%s).', peer.hostname());
1✔
1809
        await this.resolveOrphan(peer, hash);
1✔
1810
        continue;
1✔
1811
      }
1812

1813
      // Request the block if we don't have it.
1814
      if (!await this.hasBlock(hash)) {
929✔
1815
        items.push(hash);
916✔
1816
        continue;
916✔
1817
      }
1818

1819
      exists = hash;
13✔
1820

1821
      // Normally we request the hashContinue.
1822
      // In the odd case where we already have
1823
      // it, we can do one of two things: either
1824
      // force re-downloading of the block to
1825
      // continue the sync, or do a getblocks
1826
      // from the last hash.
1827
      if (i === hashes.length - 1) {
13✔
1828
        this.logger.debug('Received existing hash (%s).', peer.hostname());
2✔
1829
        await this.getBlocks(peer, hash, consensus.ZERO_HASH);
2✔
1830
      }
1831
    }
1832

1833
    // Attempt to update the peer's best height
1834
    // with the last existing hash we know of.
1835
    if (exists && this.chain.synced) {
595✔
1836
      const height = await this.chain.getHeight(exists);
5✔
1837
      if (height !== -1)
5!
1838
        peer.bestHeight = height;
5✔
1839
    }
1840

1841
    this.getBlock(peer, items);
595✔
1842
  }
1843

1844
  /**
1845
   * Handle peer inv packet (txs).
1846
   * @method
1847
   * @private
1848
   * @param {Peer} peer
1849
   * @param {Hash[]} hashes
1850
   */
1851

1852
  async handleTXInv(peer, hashes) {
1853
    assert(hashes.length > 0);
29✔
1854

1855
    if (this.syncing && !this.chain.synced)
29!
1856
      return;
×
1857

1858
    this.ensureTX(peer, hashes);
29✔
1859
  }
1860

1861
  /**
1862
   * Handle peer inv packet (claims).
1863
   * @method
1864
   * @private
1865
   * @param {Peer} peer
1866
   * @param {Hash[]} hashes
1867
   */
1868

1869
  async handleClaimInv(peer, hashes) {
1870
    assert(hashes.length > 0);
×
1871

1872
    if (this.syncing && !this.chain.synced)
×
1873
      return;
×
1874

1875
    this.ensureClaim(peer, hashes);
×
1876
  }
1877

1878
  /**
1879
   * Handle peer inv packet (airdrops).
1880
   * @method
1881
   * @private
1882
   * @param {Peer} peer
1883
   * @param {Hash[]} hashes
1884
   */
1885

1886
  async handleAirdropInv(peer, hashes) {
1887
    assert(hashes.length > 0);
×
1888

1889
    if (this.syncing && !this.chain.synced)
×
1890
      return;
×
1891

1892
    this.ensureAirdrop(peer, hashes);
×
1893
  }
1894

1895
  /**
1896
   * Handle `getdata` packet.
1897
   * @method
1898
   * @private
1899
   * @param {Peer} peer
1900
   * @param {GetDataPacket} packet
1901
   */
1902

1903
  async handleGetData(peer, packet) {
1904
    const items = packet.items;
613✔
1905

1906
    if (items.length > common.MAX_INV) {
613!
1907
      this.logger.warning(
×
1908
        'Peer sent inv with >50k items (%s).',
1909
        peer.hostname());
1910
      peer.increaseBan(100);
×
1911
      peer.destroy();
×
1912
      return;
×
1913
    }
1914

1915
    const notFound = [];
613✔
1916

1917
    let txs = 0;
613✔
1918
    let blocks = 0;
613✔
1919
    let claims = 0;
613✔
1920
    let proofs = 0;
613✔
1921
    let compact = 0;
613✔
1922
    let unknown = -1;
613✔
1923

1924
    for (const item of items) {
613✔
1925
      if (item.isTX()) {
938✔
1926
        const tx = await this.getItem(peer, item);
45✔
1927

1928
        if (!tx) {
45✔
1929
          notFound.push(item);
41✔
1930
          continue;
41✔
1931
        }
1932

1933
        // Coinbases are an insta-ban from any node.
1934
        // This should technically never happen, but
1935
        // it's worth keeping here just in case. A
1936
        // 24-hour ban from any node is rough.
1937
        if (tx.isCoinbase()) {
4!
1938
          notFound.push(item);
×
1939
          this.logger.warning('Failsafe: tried to relay a coinbase.');
×
1940
          continue;
×
1941
        }
1942

1943
        peer.send(new packets.TXPacket(tx));
4✔
1944

1945
        txs += 1;
4✔
1946

1947
        continue;
4✔
1948
      }
1949

1950
      if (item.isClaim()) {
893!
1951
        const claim = await this.getItem(peer, item);
×
1952

1953
        if (!claim) {
×
1954
          notFound.push(item);
×
1955
          continue;
×
1956
        }
1957

1958
        peer.send(new packets.ClaimPacket(claim));
×
1959

1960
        claims += 1;
×
1961

1962
        continue;
×
1963
      }
1964

1965
      if (item.isAirdrop()) {
893!
1966
        const proof = await this.getItem(peer, item);
×
1967

1968
        if (!proof) {
×
1969
          notFound.push(item);
×
1970
          continue;
×
1971
        }
1972

1973
        peer.send(new packets.AirdropPacket(proof));
×
1974

1975
        proofs += 1;
×
1976

1977
        continue;
×
1978
      }
1979

1980
      switch (item.type) {
893!
1981
        case invTypes.BLOCK: {
1982
          const result = await this.sendBlock(peer, item);
×
1983
          if (!result) {
×
1984
            notFound.push(item);
×
1985
            continue;
×
1986
          }
1987
          blocks += 1;
×
1988
          break;
×
1989
        }
1990
        case invTypes.FILTERED_BLOCK: {
1991
          if (!this.options.bip37) {
456!
1992
            this.logger.debug(
×
1993
              'Peer requested a merkleblock without bip37 enabled (%s).',
1994
              peer.hostname());
1995
            peer.destroy();
×
1996
            return;
×
1997
          }
1998

1999
          if (!peer.spvFilter) {
456!
2000
            notFound.push(item);
×
2001
            continue;
×
2002
          }
2003

2004
          const block = await this.getItem(peer, item);
456✔
2005

2006
          if (!block) {
456!
2007
            notFound.push(item);
×
2008
            continue;
×
2009
          }
2010

2011
          const merkle = block.toMerkle(peer.spvFilter);
456✔
2012

2013
          peer.send(new packets.MerkleBlockPacket(merkle));
456✔
2014

2015
          for (const tx of merkle.txs) {
456✔
2016
            peer.send(new packets.TXPacket(tx));
5✔
2017
            txs += 1;
5✔
2018
          }
2019

2020
          blocks += 1;
456✔
2021

2022
          break;
456✔
2023
        }
2024
        case invTypes.CMPCT_BLOCK: {
2025
          const height = await this.chain.getHeight(item.hash);
437✔
2026

2027
          // Fallback to full block.
2028
          if (height < this.chain.tip.height - 10) {
437✔
2029
            const result = await this.sendBlock(peer, item);
93✔
2030

2031
            if (!result) {
92!
2032
              notFound.push(item);
×
2033
              continue;
×
2034
            }
2035

2036
            blocks += 1;
92✔
2037

2038
            break;
92✔
2039
          }
2040

2041
          const block = await this.getItem(peer, item);
344✔
2042

2043
          if (!block) {
344!
2044
            notFound.push(item);
×
2045
            continue;
×
2046
          }
2047

2048
          peer.sendCompactBlock(block);
344✔
2049

2050
          blocks += 1;
344✔
2051
          compact += 1;
344✔
2052

2053
          break;
344✔
2054
        }
2055
        default: {
2056
          unknown = item.type;
×
2057
          notFound.push(item);
×
2058
          continue;
×
2059
        }
2060
      }
2061

2062
      if (item.hash.equals(peer.hashContinue)) {
892!
2063
        peer.sendInv([new InvItem(invTypes.BLOCK, this.chain.tip.hash)]);
×
2064
        peer.hashContinue = consensus.ZERO_HASH;
×
2065
      }
2066

2067
      // Wait for the peer to read
2068
      // before we pull more data
2069
      // out of the database.
2070
      await peer.drain();
892✔
2071
    }
2072

2073
    if (notFound.length > 0)
612✔
2074
      peer.send(new packets.NotFoundPacket(notFound));
25✔
2075

2076
    if (txs > 0) {
612✔
2077
      this.logger.debug(
9✔
2078
        'Served %d txs with getdata (notfound=%d) (%s).',
2079
        txs, notFound.length, peer.hostname());
2080
    }
2081

2082
    if (blocks > 0) {
612✔
2083
      this.logger.debug(
583✔
2084
        'Served %d blocks with getdata (notfound=%d, cmpct=%d) (%s).',
2085
        blocks, notFound.length, compact, peer.hostname());
2086
    }
2087

2088
    if (claims > 0) {
612!
2089
      this.logger.debug(
×
2090
        'Served %d claims with getdata (notfound=%d) (%s).',
2091
        claims, notFound.length, peer.hostname());
2092
    }
2093

2094
    if (proofs > 0) {
612!
2095
      this.logger.debug(
×
2096
        'Served %d airdrops with getdata (notfound=%d) (%s).',
2097
        proofs, notFound.length, peer.hostname());
2098
    }
2099

2100
    if (unknown !== -1) {
612!
2101
      this.logger.warning(
×
2102
        'Peer sent an unknown getdata type: %d (%d).',
2103
        unknown, peer.hostname());
2104
    }
2105
  }
2106

2107
  /**
2108
   * Handle peer notfound packet.
2109
   * @method
2110
   * @private
2111
   * @param {Peer} peer
2112
   * @param {NotFoundPacket} packet
2113
   */
2114

2115
  async handleNotFound(peer, packet) {
2116
    const items = packet.items;
25✔
2117

2118
    for (const item of items) {
25✔
2119
      if (!this.resolveItem(peer, item)) {
41!
2120
        this.logger.warning(
×
2121
          'Peer sent notfound for unrequested item: %x (%s).',
2122
          item.hash, peer.hostname());
2123
        peer.destroy();
×
2124
        return;
×
2125
      }
2126
    }
2127
  }
2128

2129
  /**
2130
   * Handle `getblocks` packet.
2131
   * @method
2132
   * @private
2133
   * @param {Peer} peer
2134
   * @param {GetBlocksPacket} packet
2135
   */
2136

2137
  async handleGetBlocks(peer, packet) {
2138
    if (!this.chain.synced)
56!
2139
      return;
×
2140

2141
    if (this.chain.options.spv)
56!
2142
      return;
×
2143

2144
    if (this.chain.options.prune)
56!
2145
      return;
×
2146

2147
    let hash = await this.chain.findLocator(packet.locator);
56✔
2148

2149
    if (hash)
56!
2150
      hash = await this.chain.getNextHash(hash);
56✔
2151

2152
    const blocks = [];
56✔
2153

2154
    while (hash) {
56✔
2155
      if (hash.equals(packet.stop))
370✔
2156
        break;
9✔
2157

2158
      blocks.push(new InvItem(invTypes.BLOCK, hash));
361✔
2159

2160
      if (blocks.length === 500) {
361!
2161
        peer.hashContinue = hash;
×
2162
        break;
×
2163
      }
2164

2165
      hash = await this.chain.getNextHash(hash);
361✔
2166
    }
2167

2168
    peer.sendInv(blocks);
56✔
2169
  }
2170

2171
  /**
2172
   * Handle `getheaders` packet.
2173
   * @method
2174
   * @private
2175
   * @param {Peer} peer
2176
   * @param {GetHeadersPacket} packet
2177
   */
2178

2179
  async handleGetHeaders(peer, packet) {
2180
    if (!this.chain.synced)
×
2181
      return;
×
2182

2183
    if (this.chain.options.spv)
×
2184
      return;
×
2185

2186
    if (this.chain.options.prune)
×
2187
      return;
×
2188

2189
    let hash;
2190
    if (packet.locator.length > 0) {
×
2191
      hash = await this.chain.findLocator(packet.locator);
×
2192
      if (hash)
×
2193
        hash = await this.chain.getNextHash(hash);
×
2194
    } else {
2195
      hash = packet.stop;
×
2196
    }
2197

2198
    let entry;
2199
    if (hash)
×
2200
      entry = await this.chain.getEntry(hash);
×
2201

2202
    const headers = [];
×
2203

2204
    while (entry) {
×
2205
      headers.push(entry.toHeaders());
×
2206

2207
      if (entry.hash.equals(packet.stop))
×
2208
        break;
×
2209

2210
      if (headers.length === 2000)
×
2211
        break;
×
2212

2213
      entry = await this.chain.getNext(entry);
×
2214
    }
2215

2216
    peer.sendHeaders(headers);
×
2217
  }
2218

2219
  /**
2220
   * Handle `headers` packet from a given peer.
2221
   * @method
2222
   * @private
2223
   * @param {Peer} peer
2224
   * @param {HeadersPacket} packet
2225
   * @returns {Promise}
2226
   */
2227

2228
  async handleHeaders(peer, packet) {
2229
    const unlock = await this.locker.lock();
×
2230
    try {
×
2231
      return await this._handleHeaders(peer, packet);
×
2232
    } finally {
2233
      unlock();
×
2234
    }
2235
  }
2236

2237
  /**
2238
   * Handle `headers` packet from
2239
   * a given peer without a lock.
2240
   * @method
2241
   * @private
2242
   * @param {Peer} peer
2243
   * @param {HeadersPacket} packet
2244
   * @returns {Promise}
2245
   */
2246

2247
  async _handleHeaders(peer, packet) {
2248
    const headers = packet.items;
×
2249

2250
    if (!this.checkpoints)
×
2251
      return;
×
2252

2253
    if (!this.syncing)
×
2254
      return;
×
2255

2256
    if (!peer.loader)
×
2257
      return;
×
2258

2259
    if (headers.length === 0)
×
2260
      return;
×
2261

2262
    if (headers.length > 2000) {
×
2263
      peer.increaseBan(100);
×
2264
      return;
×
2265
    }
2266

2267
    assert(this.headerChain.size > 0);
×
2268

2269
    let checkpoint = false;
×
2270
    let node = null;
×
2271

2272
    for (const header of headers) {
×
2273
      const last = this.headerChain.tail;
×
2274
      const hash = header.hash();
×
2275
      const height = last.height + 1;
×
2276

2277
      if (!header.verify()) {
×
2278
        this.logger.warning(
×
2279
          'Peer sent an invalid header (%s).',
2280
          peer.hostname());
2281
        peer.increaseBan(100);
×
2282
        peer.destroy();
×
2283
        return;
×
2284
      }
2285

2286
      if (!header.prevBlock.equals(last.hash)) {
×
2287
        this.logger.warning(
×
2288
          'Peer sent a bad header chain (%s).',
2289
          peer.hostname());
2290
        peer.destroy();
×
2291
        return;
×
2292
      }
2293

2294
      node = new HeaderEntry(hash, height);
×
2295

2296
      if (node.height === this.headerTip.height) {
×
2297
        if (!node.hash.equals(this.headerTip.hash)) {
×
2298
          this.logger.warning(
×
2299
            'Peer sent an invalid checkpoint (%s).',
2300
            peer.hostname());
2301
          peer.destroy();
×
2302
          return;
×
2303
        }
2304
        checkpoint = true;
×
2305
      }
2306

2307
      if (!this.headerNext)
×
2308
        this.headerNext = node;
×
2309

2310
      this.headerChain.push(node);
×
2311
    }
2312

2313
    this.logger.debug(
×
2314
      'Received %d headers from peer (%s).',
2315
      headers.length,
2316
      peer.hostname());
2317

2318
    // If we received a valid header
2319
    // chain, consider this a "block".
2320
    peer.blockTime = Date.now();
×
2321

2322
    // Request the blocks we just added.
2323
    if (checkpoint) {
×
2324
      this.headerChain.shift();
×
2325
      this.resolveHeaders(peer);
×
2326
      return;
×
2327
    }
2328

2329
    // Request more headers.
2330
    peer.sendGetHeaders([node.hash], this.headerTip.hash);
×
2331
  }
2332

2333
  /**
2334
   * Handle `sendheaders` packet.
2335
   * @method
2336
   * @private
2337
   * @param {Peer} peer
2338
   * @param {SendHeadersPacket} packet
2339
   * @returns {Promise}
2340
   */
2341

2342
  async handleSendHeaders(peer, packet) {
2343
    ;
2344
  }
2345

2346
  /**
2347
   * Handle `block` packet. Attempt to add to chain.
2348
   * @method
2349
   * @private
2350
   * @param {Peer} peer
2351
   * @param {BlockPacket} packet
2352
   * @returns {Promise}
2353
   */
2354

2355
  async handleBlock(peer, packet) {
2356
    const flags = chainCommon.flags.DEFAULT_FLAGS;
92✔
2357

2358
    if (this.options.spv) {
92!
2359
      this.logger.warning(
×
2360
        'Peer sent unsolicited block (%s).',
2361
        peer.hostname());
2362
      return;
×
2363
    }
2364

2365
    await this.addBlock(peer, packet.block, flags);
92✔
2366
  }
2367

2368
  /**
2369
   * Attempt to add block to chain.
2370
   * @method
2371
   * @private
2372
   * @param {Peer} peer
2373
   * @param {Block} block
2374
   * @returns {Promise}
2375
   */
2376

2377
  async addBlock(peer, block, flags) {
2378
    const hash = block.hash();
424✔
2379
    const unlock = await this.locker.lock(hash);
424✔
2380
    try {
424✔
2381
      return await this._addBlock(peer, block, flags);
424✔
2382
    } finally {
2383
      unlock();
424✔
2384
    }
2385
  }
2386

2387
  /**
2388
   * Attempt to add block to chain (without a lock).
2389
   * @method
2390
   * @private
2391
   * @param {Peer} peer
2392
   * @param {Block} block
2393
   * @returns {Promise}
2394
   */
2395

2396
  async _addBlock(peer, block, flags) {
2397
    if (!this.syncing)
880!
2398
      return;
×
2399

2400
    const hash = block.hash();
880✔
2401

2402
    if (!this.resolveBlock(peer, hash)) {
880!
2403
      this.logger.warning(
×
2404
        'Received unrequested block: %x (%s).',
2405
        block.hash(), peer.hostname());
2406
      peer.destroy();
×
2407
      return;
×
2408
    }
2409

2410
    peer.blockTime = Date.now();
880✔
2411

2412
    let entry;
2413
    try {
880✔
2414
      entry = await this.chain.add(block, flags, peer.id);
880✔
2415
    } catch (err) {
2416
      if (err.type === 'VerifyError') {
×
2417
        peer.reject(packets.types.BLOCK, err);
×
2418
        this.logger.warning(err);
×
2419
        return;
×
2420
      }
2421
      throw err;
×
2422
    }
2423

2424
    // Block was orphaned.
2425
    if (!entry) {
880✔
2426
      if (this.checkpoints) {
21!
2427
        this.logger.warning(
×
2428
          'Peer sent orphan block with getheaders (%s).',
2429
          peer.hostname());
2430
        return;
×
2431
      }
2432

2433
      // During a getblocks sync, peers send
2434
      // their best tip frequently. We can grab
2435
      // the height commitment from the coinbase.
2436
      const height = block.getCoinbaseHeight();
21✔
2437

2438
      if (height !== -1) {
21✔
2439
        peer.bestHash = hash;
17✔
2440
        peer.bestHeight = height;
17✔
2441
        this.resolveHeight(hash, height);
17✔
2442
      }
2443

2444
      this.logger.debug('Peer sent an orphan block. Resolving.');
21✔
2445

2446
      await this.resolveOrphan(peer, hash);
21✔
2447

2448
      return;
21✔
2449
    }
2450

2451
    if (this.chain.synced) {
859!
2452
      peer.bestHash = entry.hash;
859✔
2453
      peer.bestHeight = entry.height;
859✔
2454
      this.resolveHeight(entry.hash, entry.height);
859✔
2455
    }
2456

2457
    this.logStatus(block);
859✔
2458

2459
    await this.resolveChain(peer, hash);
859✔
2460
  }
2461

2462
  /**
2463
   * Resolve header chain.
2464
   * @method
2465
   * @private
2466
   * @param {Peer} peer
2467
   * @param {Hash} hash
2468
   * @returns {Promise}
2469
   */
2470

2471
  async resolveChain(peer, hash) {
2472
    if (!this.checkpoints)
859!
2473
      return;
859✔
2474

2475
    if (!peer.loader)
×
2476
      return;
×
2477

2478
    if (peer.destroyed)
×
2479
      throw new Error('Peer was destroyed (header chain resolution).');
×
2480

2481
    const node = this.headerChain.head;
×
2482

2483
    assert(node);
×
2484

2485
    if (!hash.equals(node.hash)) {
×
2486
      this.logger.warning(
×
2487
        'Header hash mismatch %x != %x (%s).',
2488
        hash,
2489
        node.hash,
2490
        peer.hostname());
2491

2492
      peer.destroy();
×
2493

2494
      return;
×
2495
    }
2496

2497
    if (node.height < this.network.lastCheckpoint) {
×
2498
      if (node.height === this.headerTip.height) {
×
2499
        this.logger.info(
×
2500
          'Received checkpoint %x (%d).',
2501
          node.hash, node.height);
2502

2503
        this.headerTip = this.getNextTip(node.height);
×
2504

2505
        peer.sendGetHeaders([hash], this.headerTip.hash);
×
2506

2507
        return;
×
2508
      }
2509

2510
      this.headerChain.shift();
×
2511
      this.resolveHeaders(peer);
×
2512

2513
      return;
×
2514
    }
2515

2516
    this.logger.info(
×
2517
      'Switching to getblocks (%s).',
2518
      peer.hostname());
2519

2520
    await this.switchSync(peer, hash);
×
2521
  }
2522

2523
  /**
2524
   * Switch to getblocks.
2525
   * @method
2526
   * @private
2527
   * @param {Peer} peer
2528
   * @param {Hash} hash
2529
   * @returns {Promise}
2530
   */
2531

2532
  async switchSync(peer, hash) {
2533
    assert(this.checkpoints);
×
2534

2535
    this.checkpoints = false;
×
2536
    this.headerTip = null;
×
2537
    this.headerChain.reset();
×
2538
    this.headerNext = null;
×
2539

2540
    await this.getBlocks(peer, hash, consensus.ZERO_HASH);
×
2541
  }
2542

2543
  /**
2544
   * Handle bad orphan.
2545
   * @method
2546
   * @private
2547
   * @param {Number} msg
2548
   * @param {VerifyError} err
2549
   * @param {Number} id
2550
   */
2551

2552
  handleBadOrphan(msg, err, id) {
2553
    const peer = this.peers.find(id);
×
2554

2555
    if (!peer) {
×
2556
      this.logger.warning(
×
2557
        'Could not find offending peer for orphan: %x (%d).',
2558
        err.hash, id);
2559
      return;
×
2560
    }
2561

2562
    this.logger.debug(
×
2563
      'Punishing peer for sending a bad orphan (%s).',
2564
      peer.hostname());
2565

2566
    // Punish the original peer who sent this.
2567
    peer.reject(msg, err);
×
2568
  }
2569

2570
  /**
2571
   * Log sync status.
2572
   * @private
2573
   * @param {Block} block
2574
   */
2575

2576
  logStatus(block) {
2577
    if (this.chain.height % 20 === 0) {
859✔
2578
      this.logger.debug('Status:'
36✔
2579
        + ' time=%s height=%d progress=%s'
2580
        + ' orphans=%d active=%d'
2581
        + ' target=%s peers=%d',
2582
        util.date(block.time),
2583
        this.chain.height,
2584
        (this.chain.getProgress() * 100).toFixed(2) + '%',
2585
        this.chain.orphanMap.size,
2586
        this.blockMap.size,
2587
        block.bits,
2588
        this.peers.size());
2589
    }
2590

2591
    if (this.chain.height % 2000 === 0) {
859!
2592
      this.logger.info(
×
2593
        'Received 2000 more blocks (height=%d, hash=%x).',
2594
        this.chain.height,
2595
        block.hash());
2596
    }
2597
  }
2598

2599
  /**
2600
   * Handle a transaction. Attempt to add to mempool.
2601
   * @method
2602
   * @private
2603
   * @param {Peer} peer
2604
   * @param {TXPacket} packet
2605
   * @returns {Promise}
2606
   */
2607

2608
  async handleTX(peer, packet) {
2609
    const hash = packet.tx.hash();
9✔
2610
    const unlock = await this.locker.lock(hash);
9✔
2611
    try {
9✔
2612
      return await this._handleTX(peer, packet);
9✔
2613
    } finally {
2614
      unlock();
9✔
2615
    }
2616
  }
2617

2618
  /**
2619
   * Handle a transaction. Attempt to add to mempool (without a lock).
2620
   * @method
2621
   * @private
2622
   * @param {Peer} peer
2623
   * @param {TXPacket} packet
2624
   * @returns {Promise}
2625
   */
2626

2627
  async _handleTX(peer, packet) {
2628
    const tx = packet.tx;
9✔
2629
    const hash = tx.hash();
9✔
2630
    const flags = chainCommon.flags.VERIFY_NONE;
9✔
2631
    const block = peer.merkleBlock;
9✔
2632

2633
    if (block) {
9✔
2634
      const whash = tx.hash();
5✔
2635

2636
      assert(peer.merkleMatches > 0);
5✔
2637
      assert(peer.merkleMap);
5✔
2638

2639
      if (block.hasTX(whash)) {
5!
2640
        if (peer.merkleMap.has(whash)) {
5!
2641
          this.logger.warning(
×
2642
            'Peer sent duplicate merkle tx: %x (%s).',
2643
            hash, peer.hostname());
2644
          peer.increaseBan(100);
×
2645
          return;
×
2646
        }
2647

2648
        peer.merkleMap.add(whash);
5✔
2649

2650
        block.txs.push(tx);
5✔
2651

2652
        if (--peer.merkleMatches === 0) {
5!
2653
          peer.merkleBlock = null;
5✔
2654
          peer.merkleTime = -1;
5✔
2655
          peer.merkleMatches = 0;
5✔
2656
          peer.merkleMap = null;
5✔
2657
          await this._addBlock(peer, block, flags);
5✔
2658
        }
2659

2660
        return;
5✔
2661
      }
2662
    }
2663

2664
    if (!this.resolveTX(peer, hash)) {
4!
2665
      this.logger.warning(
×
2666
        'Peer sent unrequested tx: %x (%s).',
2667
        hash, peer.hostname());
2668
      peer.destroy();
×
2669
      return;
×
2670
    }
2671

2672
    if (!this.mempool) {
4✔
2673
      this.emit('tx', tx);
2✔
2674
      return;
2✔
2675
    }
2676

2677
    let missing;
2678
    try {
2✔
2679
      missing = await this.mempool.addTX(tx, peer.id);
2✔
2680
    } catch (err) {
2681
      if (err.type === 'VerifyError') {
2!
2682
        peer.reject(packets.types.TX, err);
2✔
2683
        this.logger.info(err);
2✔
2684
        return;
2✔
2685
      }
2686
      throw err;
×
2687
    }
2688

2689
    if (missing && missing.length > 0) {
×
2690
      this.logger.debug(
×
2691
        'Requesting %d missing transactions (%s).',
2692
        missing.length, peer.hostname());
2693

2694
      this.ensureTX(peer, missing);
×
2695
    }
2696
  }
2697

2698
  /**
2699
   * Handle a claim. Attempt to add to mempool.
2700
   * @method
2701
   * @private
2702
   * @param {Peer} peer
2703
   * @param {ClaimPacket} packet
2704
   * @returns {Promise}
2705
   */
2706

2707
  async handleClaim(peer, packet) {
2708
    const hash = packet.claim.hash();
×
2709
    const unlock = await this.locker.lock(hash);
×
2710
    try {
×
2711
      return await this._handleClaim(peer, packet);
×
2712
    } finally {
2713
      unlock();
×
2714
    }
2715
  }
2716

2717
  /**
2718
   * Handle a claim. Attempt to add to mempool (without a lock).
2719
   * @method
2720
   * @private
2721
   * @param {Peer} peer
2722
   * @param {ClaimPacket} packet
2723
   * @returns {Promise}
2724
   */
2725

2726
  async _handleClaim(peer, packet) {
2727
    const claim = packet.claim;
×
2728
    const hash = claim.hash();
×
2729

2730
    if (!this.resolveClaim(peer, hash)) {
×
2731
      this.logger.warning(
×
2732
        'Peer sent unrequested claim: %x (%s).',
2733
        claim.hash(), peer.hostname());
2734
      peer.destroy();
×
2735
      return;
×
2736
    }
2737

2738
    if (!this.mempool) {
×
2739
      this.emit('claim', claim);
×
2740
      return;
×
2741
    }
2742

2743
    try {
×
2744
      await this.mempool.addClaim(claim, peer.id);
×
2745
    } catch (err) {
2746
      if (err.type === 'VerifyError') {
×
2747
        if (err.reason !== 'bad-claim-replacement')
×
2748
          peer.reject(packets.types.CLAIM, err);
×
2749
        this.logger.info(err);
×
2750
        return;
×
2751
      }
2752
      throw err;
×
2753
    }
2754
  }
2755

2756
  /**
2757
   * Handle an airdrop proof. Attempt to add to mempool.
2758
   * @method
2759
   * @private
2760
   * @param {Peer} peer
2761
   * @param {AirdropPacket} packet
2762
   * @returns {Promise}
2763
   */
2764

2765
  async handleAirdrop(peer, packet) {
2766
    const hash = packet.proof.hash();
×
2767
    const unlock = await this.locker.lock(hash);
×
2768
    try {
×
2769
      return await this._handleAirdrop(peer, packet);
×
2770
    } finally {
2771
      unlock();
×
2772
    }
2773
  }
2774

2775
  /**
2776
   * Handle an airdrop proof. Attempt to add to mempool (without a lock).
2777
   * @method
2778
   * @private
2779
   * @param {Peer} peer
2780
   * @param {AirdropPacket} packet
2781
   * @returns {Promise}
2782
   */
2783

2784
  async _handleAirdrop(peer, packet) {
2785
    const proof = packet.proof;
×
2786
    const hash = proof.hash();
×
2787

2788
    if (!this.resolveAirdrop(peer, hash)) {
×
2789
      this.logger.warning(
×
2790
        'Peer sent unrequested airdrop proof: %x (%s).',
2791
        proof.hash(), peer.hostname());
2792
      peer.destroy();
×
2793
      return;
×
2794
    }
2795

2796
    if (!this.mempool) {
×
2797
      this.emit('airdrop', proof);
×
2798
      return;
×
2799
    }
2800

2801
    try {
×
2802
      await this.mempool.addAirdrop(proof, peer.id);
×
2803
    } catch (err) {
2804
      if (err.type === 'VerifyError') {
×
2805
        peer.reject(packets.types.AIRDROP, err);
×
2806
        this.logger.info(err);
×
2807
        return;
×
2808
      }
2809
      throw err;
×
2810
    }
2811
  }
2812

2813
  /**
2814
   * Handle peer reject event.
2815
   * @method
2816
   * @private
2817
   * @param {Peer} peer
2818
   * @param {RejectPacket} packet
2819
   */
2820

2821
  async handleReject(peer, packet) {
2822
    this.logger.warning(
2✔
2823
      'Received reject (%s): msg=%s code=%s reason=%s hash=%x.',
2824
      peer.hostname(),
2825
      packets.typesByVal[packet.message] || 'UNKNOWN',
2!
2826
      packet.getCode(),
2827
      packet.reason,
2828
      packet.hash);
2829

2830
    if (!packet.hash)
2!
2831
      return;
×
2832

2833
    const entry = this.invMap.get(packet.hash);
2✔
2834

2835
    if (!entry)
2!
2836
      return;
2✔
2837

2838
    entry.handleReject(peer);
×
2839
  }
2840

2841
  /**
2842
   * Handle `mempool` packet.
2843
   * @method
2844
   * @private
2845
   * @param {Peer} peer
2846
   * @param {MempoolPacket} packet
2847
   */
2848

2849
  async handleMempool(peer, packet) {
2850
    if (!this.mempool)
39!
2851
      return;
×
2852

2853
    if (!this.chain.synced)
39!
2854
      return;
×
2855

2856
    if (!this.options.bip37) {
39!
2857
      this.logger.debug(
×
2858
        'Peer requested mempool without bip37 enabled (%s).',
2859
        peer.hostname());
2860
      peer.destroy();
×
2861
      return;
×
2862
    }
2863

2864
    const items = [];
39✔
2865

2866
    for (const hash of this.mempool.map.keys())
39✔
2867
      items.push(new InvItem(invTypes.TX, hash));
4✔
2868

2869
    this.logger.debug(
39✔
2870
      'Peer requested mempool snapshot, queing %d items (%s).',
2871
      items.length,
2872
      peer.hostname());
2873

2874
    peer.queueInv(items);
39✔
2875
  }
2876

2877
  /**
2878
   * Handle `filterload` packet.
2879
   * @method
2880
   * @private
2881
   * @param {Peer} peer
2882
   * @param {FilterLoadPacket} packet
2883
   */
2884

2885
  async handleFilterLoad(peer, packet) {
2886
    ;
2887
  }
2888

2889
  /**
2890
   * Handle `filteradd` packet.
2891
   * @method
2892
   * @private
2893
   * @param {Peer} peer
2894
   * @param {FilterAddPacket} packet
2895
   */
2896

2897
  async handleFilterAdd(peer, packet) {
2898
    ;
2899
  }
2900

2901
  /**
2902
   * Handle `filterclear` packet.
2903
   * @method
2904
   * @private
2905
   * @param {Peer} peer
2906
   * @param {FilterClearPacket} packet
2907
   */
2908

2909
  async handleFilterClear(peer, packet) {
2910
    ;
2911
  }
2912

2913
  /**
2914
   * Handle `merkleblock` packet.
2915
   * @method
2916
   * @private
2917
   * @param {Peer} peer
2918
   * @param {MerkleBlockPacket} block
2919
   */
2920

2921
  async handleMerkleBlock(peer, packet) {
2922
    const hash = packet.block.hash();
456✔
2923
    const unlock = await this.locker.lock(hash);
456✔
2924
    try {
456✔
2925
      return await this._handleMerkleBlock(peer, packet);
456✔
2926
    } finally {
2927
      unlock();
456✔
2928
    }
2929
  }
2930

2931
  /**
2932
   * Handle `merkleblock` packet (without a lock).
2933
   * @method
2934
   * @private
2935
   * @param {Peer} peer
2936
   * @param {MerkleBlockPacket} block
2937
   */
2938

2939
  async _handleMerkleBlock(peer, packet) {
2940
    if (!this.syncing)
456!
2941
      return;
×
2942

2943
    // Potential DoS.
2944
    if (!this.options.spv) {
456!
2945
      this.logger.warning(
×
2946
        'Peer sent unsolicited merkleblock (%s).',
2947
        peer.hostname());
2948
      peer.increaseBan(100);
×
2949
      return;
×
2950
    }
2951

2952
    const block = packet.block;
456✔
2953
    const hash = block.hash();
456✔
2954

2955
    if (!peer.blockMap.has(hash)) {
456!
2956
      this.logger.warning(
×
2957
        'Peer sent an unrequested merkleblock (%s).',
2958
        peer.hostname());
2959
      peer.destroy();
×
2960
      return;
×
2961
    }
2962

2963
    if (peer.merkleBlock) {
456!
2964
      this.logger.warning(
×
2965
        'Peer sent a merkleblock prematurely (%s).',
2966
        peer.hostname());
2967
      peer.increaseBan(100);
×
2968
      return;
×
2969
    }
2970

2971
    if (!block.verify()) {
456!
2972
      this.logger.warning(
×
2973
        'Peer sent an invalid merkleblock (%s).',
2974
        peer.hostname());
2975
      peer.increaseBan(100);
×
2976
      return;
×
2977
    }
2978

2979
    const tree = block.getTree();
456✔
2980

2981
    if (tree.matches.length === 0) {
456✔
2982
      const flags = chainCommon.flags.VERIFY_NONE;
451✔
2983
      await this._addBlock(peer, block, flags);
451✔
2984
      return;
451✔
2985
    }
2986

2987
    peer.merkleBlock = block;
5✔
2988
    peer.merkleTime = Date.now();
5✔
2989
    peer.merkleMatches = tree.matches.length;
5✔
2990
    peer.merkleMap = new BufferSet();
5✔
2991
  }
2992

2993
  /**
2994
   * Handle `sendcmpct` packet.
2995
   * @method
2996
   * @private
2997
   * @param {Peer} peer
2998
   * @param {FeeFilterPacket} packet
2999
   */
3000

3001
  async handleFeeFilter(peer, packet) {
3002
    ;
3003
  }
3004

3005
  /**
3006
   * Handle `sendcmpct` packet.
3007
   * @method
3008
   * @private
3009
   * @param {Peer} peer
3010
   * @param {SendCmpctPacket} packet
3011
   */
3012

3013
  async handleSendCmpct(peer, packet) {
3014
    ;
3015
  }
3016

3017
  /**
3018
   * Handle `cmpctblock` packet.
3019
   * @method
3020
   * @private
3021
   * @param {Peer} peer
3022
   * @param {CompactBlockPacket} packet
3023
   */
3024

3025
  async handleCmpctBlock(peer, packet) {
3026
    const block = packet.block;
332✔
3027
    const hash = block.hash();
332✔
3028

3029
    if (!this.syncing)
332!
3030
      return;
×
3031

3032
    if (!this.options.compact) {
332!
3033
      this.logger.info(
×
3034
        'Peer sent unsolicited cmpctblock (%s).',
3035
        peer.hostname());
3036
      this.destroy();
×
3037
      return;
×
3038
    }
3039

3040
    if (!peer.hasCompact()) {
332!
3041
      this.logger.info(
×
3042
        'Peer sent unsolicited cmpctblock (%s).',
3043
        peer.hostname());
3044
      this.destroy();
×
3045
      return;
×
3046
    }
3047

3048
    if (peer.compactBlocks.has(hash)) {
332!
3049
      this.logger.debug(
×
3050
        'Peer sent us a duplicate compact block (%s).',
3051
        peer.hostname());
3052
      return;
×
3053
    }
3054

3055
    if (this.compactBlocks.has(hash)) {
332!
3056
      this.logger.debug(
×
3057
        'Already waiting for compact block %x (%s).',
3058
        hash, peer.hostname());
3059
      return;
×
3060
    }
3061

3062
    if (!peer.blockMap.has(hash)) {
332!
3063
      if (this.options.blockMode !== 1) {
×
3064
        this.logger.warning(
×
3065
          'Peer sent us an unrequested compact block (%s).',
3066
          peer.hostname());
3067
        peer.destroy();
×
3068
        return;
×
3069
      }
3070
      peer.blockMap.set(hash, Date.now());
×
3071
      assert(!this.blockMap.has(hash));
×
3072
      this.blockMap.add(hash);
×
3073
    }
3074

3075
    if (!this.mempool) {
332!
3076
      this.logger.warning('Requesting compact blocks without a mempool!');
×
3077
      return;
×
3078
    }
3079

3080
    if (!block.verify()) {
332!
3081
      this.logger.debug(
×
3082
        'Peer sent an invalid compact block (%s).',
3083
        peer.hostname());
3084
      peer.increaseBan(100);
×
3085
      return;
×
3086
    }
3087

3088
    let result;
3089
    try {
332✔
3090
      result = block.init();
332✔
3091
    } catch (e) {
3092
      this.logger.debug(
×
3093
        'Peer sent an invalid compact block (%s).',
3094
        peer.hostname());
3095
      peer.increaseBan(100);
×
3096
      return;
×
3097
    }
3098

3099
    if (!result) {
332!
3100
      this.logger.warning(
×
3101
        'Siphash collision for %x. Requesting full block (%s).',
3102
        block.hash(), peer.hostname());
3103
      peer.getFullBlock(hash);
×
3104
      peer.increaseBan(10);
×
3105
      return;
×
3106
    }
3107

3108
    const full = block.fillMempool(this.mempool);
332✔
3109

3110
    if (full) {
332✔
3111
      this.logger.debug(
294✔
3112
        'Received full compact block %x (%s).',
3113
        block.hash(), peer.hostname());
3114
      const flags = chainCommon.flags.VERIFY_BODY;
294✔
3115
      await this.addBlock(peer, block.toBlock(), flags);
294✔
3116
      return;
294✔
3117
    }
3118

3119
    if (peer.compactBlocks.size >= 15) {
38!
3120
      this.logger.warning('Compact block DoS attempt (%s).', peer.hostname());
×
3121
      peer.destroy();
×
3122
      return;
×
3123
    }
3124

3125
    block.now = Date.now();
38✔
3126

3127
    assert(!peer.compactBlocks.has(hash));
38✔
3128
    peer.compactBlocks.set(hash, block);
38✔
3129

3130
    this.compactBlocks.add(hash);
38✔
3131

3132
    this.logger.debug(
38✔
3133
      'Received non-full compact block %x tx=%d/%d (%s).',
3134
      block.hash(), block.count, block.totalTX, peer.hostname());
3135

3136
    peer.send(new packets.GetBlockTxnPacket(block.toRequest()));
38✔
3137
  }
3138

3139
  /**
3140
   * Handle `getblocktxn` packet.
3141
   * @method
3142
   * @private
3143
   * @param {Peer} peer
3144
   * @param {GetBlockTxnPacket} packet
3145
   */
3146

3147
  async handleGetBlockTxn(peer, packet) {
3148
    const req = packet.request;
38✔
3149

3150
    if (this.chain.options.spv)
38!
3151
      return;
×
3152

3153
    if (this.chain.options.prune)
38!
3154
      return;
×
3155

3156
    const item = new InvItem(invTypes.BLOCK, req.hash);
38✔
3157

3158
    const block = await this.getItem(peer, item);
38✔
3159

3160
    if (!block) {
38!
3161
      this.logger.debug(
×
3162
        'Peer sent getblocktxn for non-existent block (%s).',
3163
        peer.hostname());
3164
      peer.increaseBan(100);
×
3165
      return;
×
3166
    }
3167

3168
    const height = await this.chain.getHeight(req.hash);
38✔
3169

3170
    if (height < this.chain.tip.height - 15) {
38!
3171
      this.logger.debug(
×
3172
        'Peer sent a getblocktxn for a block > 15 deep (%s)',
3173
        peer.hostname());
3174
      return;
×
3175
    }
3176

3177
    this.logger.debug(
38✔
3178
      'Sending blocktxn for %x to peer (%s).',
3179
      block.hash(),
3180
      peer.hostname());
3181

3182
    const res = BIP152.TXResponse.fromBlock(block, req);
38✔
3183

3184
    peer.send(new packets.BlockTxnPacket(res));
38✔
3185
  }
3186

3187
  /**
3188
   * Handle `blocktxn` packet.
3189
   * @method
3190
   * @private
3191
   * @param {Peer} peer
3192
   * @param {BlockTxnPacket} packet
3193
   */
3194

3195
  async handleBlockTxn(peer, packet) {
3196
    const res = packet.response;
38✔
3197
    const block = peer.compactBlocks.get(res.hash);
38✔
3198
    const flags = chainCommon.flags.VERIFY_BODY;
38✔
3199

3200
    if (!block) {
38!
3201
      this.logger.debug(
×
3202
        'Peer sent unsolicited blocktxn (%s).',
3203
        peer.hostname());
3204
      return;
×
3205
    }
3206

3207
    peer.compactBlocks.delete(res.hash);
38✔
3208

3209
    assert(this.compactBlocks.has(res.hash));
38✔
3210
    this.compactBlocks.delete(res.hash);
38✔
3211

3212
    if (!block.fillMissing(res)) {
38!
3213
      this.logger.warning(
×
3214
        'Peer sent non-full blocktxn for %x. Requesting full block (%s).',
3215
        block.hash(),
3216
        peer.hostname());
3217
      peer.getFullBlock(res.hash);
×
3218
      peer.increaseBan(10);
×
3219
      return;
×
3220
    }
3221

3222
    this.logger.debug(
38✔
3223
      'Filled compact block %x (%s).',
3224
      block.hash(), peer.hostname());
3225

3226
    await this.addBlock(peer, block.toBlock(), flags);
38✔
3227
  }
3228

3229
  /**
3230
   * Handle `getproof` packet.
3231
   * @method
3232
   * @private
3233
   * @param {Peer} peer
3234
   * @param {GetProofPacket} packet
3235
   */
3236

3237
  async handleGetProof(peer, packet) {
3238
    const {root, key} = packet;
54✔
3239
    const proof = await this.chain.db.prove(root, key);
54✔
3240
    peer.sendProof(root, key, proof);
53✔
3241
  }
3242

3243
  /**
3244
   * Handle `proof` packet.
3245
   * @method
3246
   * @private
3247
   * @param {Peer} peer
3248
   * @param {ProofPacket} packet
3249
   */
3250

3251
  async handleProof(peer, packet) {
3252
    const {root, key, proof} = packet;
52✔
3253

3254
    if (!peer.nameMap.has(key)) {
52!
3255
      this.logger.warning(
×
3256
        'Peer sent us an unsolicited proof: %x/%x (%s)!',
3257
        key,
3258
        root,
3259
        peer.hostname());
3260
      peer.increaseBan(100);
×
3261
      return;
×
3262
    }
3263

3264
    const item = this.nameMap.get(key);
52✔
3265
    assert(item);
52✔
3266

3267
    if (!item.root.equals(root)) {
52!
3268
      this.logger.warning(
×
3269
        'Peer sent us an unsolicited proof: %x/%x (%s)!',
3270
        key,
3271
        root,
3272
        peer.hostname());
3273
      peer.increaseBan(100);
×
3274
      return;
×
3275
    }
3276

3277
    if (proof.value && proof.value.length > NameState.MAX_SIZE) {
52!
3278
      this.logger.warning(
×
3279
        'Peer sent us an invalid data length: %x/%x (%s)!',
3280
        key,
3281
        root,
3282
        peer.hostname());
3283
      peer.increaseBan(100);
×
3284
      return;
×
3285
    }
3286

3287
    const [code, data] = proof.verify(root, key, blake2b, 256);
52✔
3288

3289
    if (code !== 0) {
52!
3290
      this.logger.warning(
×
3291
        'Peer sent us an invalid proof: %x/%x/%d (%s)!',
3292
        key,
3293
        root,
3294
        code,
3295
        peer.hostname());
3296
      peer.increaseBan(100);
×
3297
      return;
×
3298
    }
3299

3300
    peer.totalProofs += 1;
52✔
3301
    peer.nameMap.delete(key);
52✔
3302
    this.nameMap.delete(key);
52✔
3303

3304
    item.resolve(data);
52✔
3305
  }
3306

3307
  /**
3308
   * Handle `unknown` packet.
3309
   * @method
3310
   * @private
3311
   * @param {Peer} peer
3312
   * @param {UnknownPacket} packet
3313
   */
3314

3315
  async handleUnknown(peer, packet) {
3316
    this.logger.warning(
×
3317
      'Unknown packet: %d (%s).',
3318
      packet.rawType, peer.hostname());
3319
  }
3320

3321
  /**
3322
   * Create an inbound peer from an existing socket.
3323
   * @private
3324
   * @param {net.Socket} socket
3325
   */
3326

3327
  addInbound(socket, encrypted) {
3328
    if (!this.opened) {
45!
3329
      socket.destroy();
×
3330
      return;
×
3331
    }
3332

3333
    const peer = this.createInbound(socket, encrypted);
45✔
3334

3335
    this.logger.info('Added inbound peer (%s).', peer.hostname());
45✔
3336

3337
    this.peers.add(peer);
45✔
3338
  }
3339

3340
  /**
3341
   * Allocate a host from the host list.
3342
   * @returns {NetAddress}
3343
   */
3344

3345
  getHost() {
3346
    for (const addr of this.hosts.nodes) {
169✔
3347
      if (this.peers.has(addr.hostname))
13!
3348
        continue;
×
3349

3350
      return addr;
13✔
3351
    }
3352

3353
    const services = this.options.getRequiredServices();
156✔
3354
    const now = this.network.now();
156✔
3355

3356
    // Calculate maximum number of hosts we can get.
3357
    let max = this.hosts.totalFresh + this.hosts.totalUsed;
156✔
3358

3359
    // We don't want to loop a lot here as it's expensive on CPU.
3360
    // If this gets high, such as 100, it could cause a local DoS
3361
    // for incoming RPC calls.
3362
    if (max > 10)
156✔
3363
      max = 10;
24✔
3364

3365
    // Work out a percentage based hit rate outside of the
3366
    // loop to save CPU.
3367
    // Subtract 1 because we're zero based.
3368
    const pc1  = max / 100;
156✔
3369
    const pc30 = (pc1 * 30) - 1;
156✔
3370
    const pc50 = (pc1 * 50) - 1;
156✔
3371

3372
    for (let i = 0; i < max; i++) {
156✔
3373
      const entry = this.hosts.getHost();
104✔
3374

3375
      if (!entry)
104!
3376
        break;
×
3377

3378
      const addr = entry.addr;
104✔
3379

3380
      if (this.peers.has(addr.hostname))
104✔
3381
        continue;
36✔
3382

3383
      if (this.hosts.local.has(addr.hostname))
68!
3384
        continue;
×
3385

3386
      if (this.hosts.isBanned(addr.host))
68!
3387
        continue;
×
3388

3389
      if (!addr.isValid())
68!
3390
        continue;
×
3391

3392
      if (!addr.hasServices(services))
68!
3393
        continue;
×
3394

3395
      if (!this.options.onion && addr.isOnion())
68!
3396
        continue;
×
3397

3398
      if (this.options.brontideOnly && !addr.hasKey())
68!
3399
        continue;
×
3400

3401
      // Don't connect to outbound peers in the same group.
3402
      if (this.connectedGroups.has(addr.getGroup()))
68!
UNCOV
3403
        continue;
×
3404

3405
      if (i < pc30 && now - entry.lastAttempt < 600)
68!
3406
        continue;
×
3407

3408
      if (i < pc50 && addr.port !== this.network.port)
68✔
3409
        continue;
8✔
3410

3411
      return entry.addr;
60✔
3412
    }
3413

3414
    return null;
96✔
3415
  }
3416

3417
  /**
3418
   * Create an outbound non-loader peer. These primarily
3419
   * exist for transaction relaying.
3420
   * @private
3421
   */
3422

3423
  addOutbound() {
3424
    if (!this.opened)
57!
3425
      return;
×
3426

3427
    if (this.peers.outbound >= this.options.maxOutbound)
57!
3428
      return;
×
3429

3430
    // Hang back if we don't
3431
    // have a loader peer yet.
3432
    if (!this.peers.load)
57!
3433
      return;
×
3434

3435
    const addr = this.getHost();
57✔
3436

3437
    if (!addr)
57✔
3438
      return;
36✔
3439

3440
    const peer = this.createOutbound(addr);
21✔
3441

3442
    this.peers.add(peer);
21✔
3443
    this.connectedGroups.add(addr.getGroup());
21✔
3444

3445
    this.emit('peer', peer);
21✔
3446
  }
3447

3448
  /**
3449
   * Attempt to refill the pool with peers (no lock).
3450
   * @private
3451
   */
3452

3453
  fillOutbound() {
3454
    const total = this.hosts.size() + this.hosts.nodes.length;
113✔
3455

3456
    if (!this.peers.load)
113✔
3457
      this.addLoader();
112✔
3458

3459
    let need = this.options.maxOutbound - this.peers.outbound;
113✔
3460

3461
    if (need > total)
113✔
3462
      need = total;
96✔
3463

3464
    if (need <= 0)
113✔
3465
      return;
74✔
3466

3467
    this.logger.spam('Refilling %d peers (%d/%d).',
39✔
3468
      need,
3469
      this.peers.outbound,
3470
      this.options.maxOutbound);
3471

3472
    for (let i = 0; i < need; i++)
39✔
3473
      this.addOutbound();
57✔
3474
  }
3475

3476
  /**
3477
   * Attempt to refill the pool with peers (no lock).
3478
   * @private
3479
   */
3480

3481
  refill() {
3482
    try {
3✔
3483
      this.fillOutbound();
3✔
3484
    } catch (e) {
3485
      this.emit('error', e);
×
3486
    }
3487
  }
3488

3489
  /**
3490
   * Remove a peer from any list. Drop all load requests.
3491
   * @private
3492
   * @param {Peer} peer
3493
   */
3494

3495
  removePeer(peer) {
3496
    this.peers.remove(peer);
120✔
3497

3498
    if (peer.outbound)
120✔
3499
      this.connectedGroups.delete(peer.address.getGroup());
75✔
3500

3501
    for (const hash of peer.blockMap.keys())
120✔
3502
      this.resolveBlock(peer, hash);
14✔
3503

3504
    for (const hash of peer.txMap.keys())
120✔
3505
      this.resolveTX(peer, hash);
×
3506

3507
    for (const hash of peer.claimMap.keys())
120✔
3508
      this.resolveClaim(peer, hash);
×
3509

3510
    for (const hash of peer.airdropMap.keys())
120✔
3511
      this.resolveAirdrop(peer, hash);
×
3512

3513
    for (const hash of peer.compactBlocks.keys()) {
120✔
3514
      assert(this.compactBlocks.has(hash));
×
3515
      this.compactBlocks.delete(hash);
×
3516
    }
3517

3518
    peer.compactBlocks.clear();
120✔
3519

3520
    for (const hash of peer.nameMap.keys()) {
120✔
3521
      const item = this.nameMap.get(hash);
2✔
3522
      assert(item);
2✔
3523

3524
      item.reject(new Error('Peer removed.'));
2✔
3525

3526
      this.nameMap.delete(hash);
2✔
3527
    }
3528

3529
    peer.nameMap.clear();
120✔
3530
  }
3531

3532
  /**
3533
   * Ban peer.
3534
   * @param {NetAddress} addr
3535
   */
3536

3537
  ban(addr) {
3538
    const peer = this.peers.get(addr.hostname);
1✔
3539

3540
    this.logger.debug('Banning peer (%s).', addr.hostname);
1✔
3541

3542
    this.hosts.ban(addr.host);
1✔
3543
    this.hosts.remove(addr.hostname);
1✔
3544

3545
    if (peer)
1!
3546
      peer.destroy();
1✔
3547
  }
3548

3549
  /**
3550
   * Unban peer.
3551
   * @param {NetAddress} addr
3552
   */
3553

3554
  unban(addr) {
3555
    this.hosts.unban(addr.host);
×
3556
  }
3557

3558
  /**
3559
   * Set the spv filter.
3560
   * @param {BloomFilter} filter
3561
   */
3562

3563
  setFilter(filter) {
3564
    if (!this.options.spv)
78✔
3565
      return;
72✔
3566

3567
    this.spvFilter = filter;
6✔
3568
    this.queueFilterLoad();
6✔
3569
  }
3570

3571
  /**
3572
   * Watch a an address hash (filterload, SPV-only).
3573
   * @param {Buffer|Hash} data
3574
   */
3575

3576
  watch(data, enc) {
3577
    if (!this.options.spv)
1!
3578
      return;
×
3579

3580
    this.spvFilter.add(data, enc);
1✔
3581
    this.queueFilterLoad();
1✔
3582
  }
3583

3584
  /**
3585
   * Reset the spv filter (filterload, SPV-only).
3586
   */
3587

3588
  unwatch() {
3589
    if (!this.options.spv)
×
3590
      return;
×
3591

3592
    this.spvFilter.reset();
×
3593
    this.queueFilterLoad();
×
3594
  }
3595

3596
  /**
3597
   * Queue a resend of the bloom filter.
3598
   */
3599

3600
  queueFilterLoad() {
3601
    if (!this.options.spv)
147,690✔
3602
      return;
147,278✔
3603

3604
    if (this.pendingFilter != null)
412✔
3605
      return;
404✔
3606

3607
    this.pendingFilter = setTimeout(() => {
8✔
3608
      this.pendingFilter = null;
3✔
3609
      this.sendFilterLoad();
3✔
3610
    }, 100);
3611
  }
3612

3613
  /**
3614
   * Resend the bloom filter to peers.
3615
   */
3616

3617
  sendFilterLoad() {
3618
    if (!this.options.spv)
1!
3619
      return;
×
3620

3621
    assert(this.spvFilter);
1✔
3622

3623
    for (let peer = this.peers.head(); peer; peer = peer.next)
1✔
3624
      peer.sendFilterLoad(this.spvFilter);
1✔
3625
  }
3626

3627
  /**
3628
   * Add an address to the bloom filter (SPV-only).
3629
   * @param {Address|AddressString} address
3630
   */
3631

3632
  watchAddress(address) {
3633
    const hash = Address.getHash(address);
×
3634
    this.watch(hash);
×
3635
  }
3636

3637
  /**
3638
   * Add an outpoint to the bloom filter (SPV-only).
3639
   * @param {Outpoint} outpoint
3640
   */
3641

3642
  watchOutpoint(outpoint) {
3643
    this.watch(outpoint.encode());
×
3644
  }
3645

3646
  /**
3647
   * Add a nameHash to the bloom filter (SPV-only).
3648
   * @param {Hash} nameHash
3649
   */
3650

3651
  watchName(nameHash) {
3652
    this.watch(nameHash);
1✔
3653
  }
3654

3655
  /**
3656
   * Send `getblocks` to peer after building
3657
   * locator and resolving orphan root.
3658
   * @method
3659
   * @param {Peer} peer
3660
   * @param {Hash} orphan - Orphan hash to resolve.
3661
   * @returns {Promise}
3662
   */
3663

3664
  async resolveOrphan(peer, orphan) {
3665
    const locator = await this.chain.getLocator();
22✔
3666
    const root = this.chain.getOrphanRoot(orphan);
22✔
3667

3668
    assert(root);
22✔
3669

3670
    peer.sendGetBlocks(locator, root);
22✔
3671
  }
3672

3673
  /**
3674
   * Send `getheaders` to peer after building locator.
3675
   * @method
3676
   * @param {Peer} peer
3677
   * @param {Hash} tip - Tip to build chain locator from.
3678
   * @param {Hash} stop
3679
   * @returns {Promise}
3680
   */
3681

3682
  async getHeaders(peer, tip, stop) {
3683
    const locator = await this.chain.getLocator(tip);
×
3684
    peer.sendGetHeaders(locator, stop);
×
3685
  }
3686

3687
  /**
3688
   * Send `getblocks` to peer after building locator.
3689
   * @method
3690
   * @param {Peer} peer
3691
   * @param {Hash} tip - Tip hash to build chain locator from.
3692
   * @param {Hash} stop
3693
   * @returns {Promise}
3694
   */
3695

3696
  async getBlocks(peer, tip, stop) {
3697
    const locator = await this.chain.getLocator(tip);
2✔
3698
    peer.sendGetBlocks(locator, stop);
2✔
3699
  }
3700

3701
  /**
3702
   * Queue a `getdata` request to be sent.
3703
   * @param {Peer} peer
3704
   * @param {Hash[]} hashes
3705
   */
3706

3707
  getBlock(peer, hashes) {
3708
    if (!this.opened)
595!
3709
      return;
×
3710

3711
    if (!peer.handshake)
595!
3712
      throw new Error('Peer handshake not complete (getdata).');
×
3713

3714
    if (peer.destroyed)
595!
3715
      throw new Error('Peer is destroyed (getdata).');
×
3716

3717
    let now = Date.now();
595✔
3718

3719
    const items = [];
595✔
3720

3721
    for (const hash of hashes) {
595✔
3722
      if (this.blockMap.has(hash))
916✔
3723
        continue;
22✔
3724

3725
      this.blockMap.add(hash);
894✔
3726
      peer.blockMap.set(hash, now);
894✔
3727

3728
      if (this.chain.synced)
894!
3729
        now += 100;
894✔
3730

3731
      items.push(hash);
894✔
3732
    }
3733

3734
    if (items.length === 0)
595✔
3735
      return;
10✔
3736

3737
    if (peer.blockMap.size >= common.MAX_BLOCK_REQUEST) {
585!
3738
      this.logger.warning(
×
3739
        'Peer advertised too many blocks (%s).',
3740
        peer.hostname());
3741
      peer.destroy();
×
3742
      return;
×
3743
    }
3744

3745
    this.logger.debug(
585✔
3746
      'Requesting %d/%d blocks from peer with getdata (%s).',
3747
      items.length,
3748
      this.blockMap.size,
3749
      peer.hostname());
3750

3751
    peer.getBlock(items);
585✔
3752
  }
3753

3754
  /**
3755
   * Queue a `getdata` request to be sent.
3756
   * @param {Peer} peer
3757
   * @param {Hash[]} hashes
3758
   */
3759

3760
  getTX(peer, hashes) {
3761
    if (!this.opened)
29!
3762
      return;
×
3763

3764
    if (!peer.handshake)
29!
3765
      throw new Error('Peer handshake not complete (getdata).');
×
3766

3767
    if (peer.destroyed)
29!
3768
      throw new Error('Peer is destroyed (getdata).');
×
3769

3770
    let now = Date.now();
29✔
3771

3772
    const items = [];
29✔
3773

3774
    for (const hash of hashes) {
29✔
3775
      if (this.txMap.has(hash))
45!
3776
        continue;
×
3777

3778
      this.txMap.add(hash);
45✔
3779
      peer.txMap.set(hash, now);
45✔
3780

3781
      now += 50;
45✔
3782

3783
      items.push(hash);
45✔
3784
    }
3785

3786
    if (items.length === 0)
29!
3787
      return;
×
3788

3789
    if (peer.txMap.size >= common.MAX_TX_REQUEST) {
29!
3790
      this.logger.warning(
×
3791
        'Peer advertised too many txs (%s).',
3792
        peer.hostname());
3793
      peer.destroy();
×
3794
      return;
×
3795
    }
3796

3797
    this.logger.debug(
29✔
3798
      'Requesting %d/%d txs from peer with getdata (%s).',
3799
      items.length,
3800
      this.txMap.size,
3801
      peer.hostname());
3802

3803
    peer.getTX(items);
29✔
3804
  }
3805

3806
  /**
3807
   * Queue a `getdata` request to be sent.
3808
   * @param {Peer} peer
3809
   * @param {Hash[]} hashes
3810
   */
3811

3812
  getClaim(peer, hashes) {
3813
    if (!this.opened)
×
3814
      return;
×
3815

3816
    if (!peer.handshake)
×
3817
      throw new Error('Peer handshake not complete (getdata).');
×
3818

3819
    if (peer.destroyed)
×
3820
      throw new Error('Peer is destroyed (getdata).');
×
3821

3822
    let now = Date.now();
×
3823

3824
    const items = [];
×
3825

3826
    for (const hash of hashes) {
×
3827
      if (this.claimMap.has(hash))
×
3828
        continue;
×
3829

3830
      this.claimMap.add(hash);
×
3831
      peer.claimMap.set(hash, now);
×
3832

3833
      now += 50;
×
3834

3835
      items.push(hash);
×
3836
    }
3837

3838
    if (items.length === 0)
×
3839
      return;
×
3840

3841
    if (peer.claimMap.size >= common.MAX_CLAIM_REQUEST) {
×
3842
      this.logger.warning(
×
3843
        'Peer advertised too many txs (%s).',
3844
        peer.hostname());
3845
      peer.destroy();
×
3846
      return;
×
3847
    }
3848

3849
    this.logger.debug(
×
3850
      'Requesting %d/%d claims from peer with getdata (%s).',
3851
      items.length,
3852
      this.claimMap.size,
3853
      peer.hostname());
3854

3855
    peer.getClaim(items);
×
3856
  }
3857

3858
  /**
3859
   * Queue a `getdata` request to be sent.
3860
   * @param {Peer} peer
3861
   * @param {Hash[]} hashes
3862
   */
3863

3864
  getAirdrop(peer, hashes) {
3865
    if (!this.opened)
×
3866
      return;
×
3867

3868
    if (!peer.handshake)
×
3869
      throw new Error('Peer handshake not complete (getdata).');
×
3870

3871
    if (peer.destroyed)
×
3872
      throw new Error('Peer is destroyed (getdata).');
×
3873

3874
    let now = Date.now();
×
3875

3876
    const items = [];
×
3877

3878
    for (const hash of hashes) {
×
3879
      if (this.airdropMap.has(hash))
×
3880
        continue;
×
3881

3882
      this.airdropMap.add(hash);
×
3883
      peer.airdropMap.set(hash, now);
×
3884

3885
      now += 50;
×
3886

3887
      items.push(hash);
×
3888
    }
3889

3890
    if (items.length === 0)
×
3891
      return;
×
3892

3893
    if (peer.airdropMap.size >= common.MAX_CLAIM_REQUEST) {
×
3894
      this.logger.warning(
×
3895
        'Peer advertised too many txs (%s).',
3896
        peer.hostname());
3897
      peer.destroy();
×
3898
      return;
×
3899
    }
3900

3901
    this.logger.debug(
×
3902
      'Requesting %d/%d airdrops from peer with getdata (%s).',
3903
      items.length,
3904
      this.airdropMap.size,
3905
      peer.hostname());
3906

3907
    peer.getAirdrop(items);
×
3908
  }
3909

3910
  /**
3911
   * Test whether the chain has or has seen an item.
3912
   * @method
3913
   * @param {Hash} hash
3914
   * @returns {Promise} - Returns Boolean.
3915
   */
3916

3917
  async hasBlock(hash) {
3918
    // Check the lock.
3919
    if (this.locker.has(hash))
929!
3920
      return true;
×
3921

3922
    // Check the chain.
3923
    if (await this.chain.has(hash))
929✔
3924
      return true;
13✔
3925

3926
    return false;
916✔
3927
  }
3928

3929
  /**
3930
   * Test whether the mempool has or has seen an item.
3931
   * @param {Hash} hash
3932
   * @returns {Boolean}
3933
   */
3934

3935
  hasTX(hash) {
3936
    // Check the lock queue.
3937
    if (this.locker.has(hash))
45!
3938
      return true;
×
3939

3940
    if (!this.mempool) {
45✔
3941
      // Check the TX filter if
3942
      // we don't have a mempool.
3943
      if (!this.txFilter.added(hash))
3!
3944
        return true;
×
3945
    } else {
3946
      // Check the mempool.
3947
      if (this.mempool.has(hash))
42!
3948
        return true;
×
3949

3950
      // If we recently rejected this item. Ignore.
3951
      if (this.mempool.hasReject(hash)) {
42!
3952
        this.logger.spam('Saw known reject of %x.', hash);
×
3953
        return true;
×
3954
      }
3955
    }
3956

3957
    return false;
45✔
3958
  }
3959

3960
  /**
3961
   * Test whether the mempool has or has seen an item.
3962
   * @param {Hash} hash
3963
   * @returns {Boolean}
3964
   */
3965

3966
  hasClaim(hash) {
3967
    return this.hasTX(hash);
×
3968
  }
3969

3970
  /**
3971
   * Test whether the mempool has or has seen an item.
3972
   * @param {Hash} hash
3973
   * @returns {Boolean}
3974
   */
3975

3976
  hasAirdrop(hash) {
3977
    return this.hasTX(hash);
×
3978
  }
3979

3980
  /**
3981
   * Queue a `getdata` request to be sent.
3982
   * Check tx existence before requesting.
3983
   * @param {Peer} peer
3984
   * @param {Hash[]} hashes
3985
   */
3986

3987
  ensureTX(peer, hashes) {
3988
    const items = [];
29✔
3989

3990
    for (const hash of hashes) {
29✔
3991
      if (this.hasTX(hash))
45!
3992
        continue;
×
3993

3994
      items.push(hash);
45✔
3995
    }
3996

3997
    this.getTX(peer, items);
29✔
3998
  }
3999

4000
  /**
4001
   * Queue a `getdata` request to be sent.
4002
   * Check tx existence before requesting.
4003
   * @param {Peer} peer
4004
   * @param {Hash[]} hashes
4005
   */
4006

4007
  ensureClaim(peer, hashes) {
4008
    const items = [];
×
4009

4010
    for (const hash of hashes) {
×
4011
      if (this.hasClaim(hash))
×
4012
        continue;
×
4013

4014
      items.push(hash);
×
4015
    }
4016

4017
    this.getClaim(peer, items);
×
4018
  }
4019

4020
  /**
4021
   * Queue a `getdata` request to be sent.
4022
   * Check tx existence before requesting.
4023
   * @param {Peer} peer
4024
   * @param {Hash[]} hashes
4025
   */
4026

4027
  ensureAirdrop(peer, hashes) {
4028
    const items = [];
×
4029

4030
    for (const hash of hashes) {
×
4031
      if (this.hasAirdrop(hash))
×
4032
        continue;
×
4033

4034
      items.push(hash);
×
4035
    }
4036

4037
    this.getAirdrop(peer, items);
×
4038
  }
4039

4040
  /**
4041
   * Fulfill a requested tx.
4042
   * @param {Peer} peer
4043
   * @param {Hash} hash
4044
   * @returns {Boolean}
4045
   */
4046

4047
  resolveTX(peer, hash) {
4048
    if (!peer.txMap.has(hash))
45!
4049
      return false;
×
4050

4051
    peer.txMap.delete(hash);
45✔
4052

4053
    assert(this.txMap.has(hash));
45✔
4054
    this.txMap.delete(hash);
45✔
4055

4056
    return true;
45✔
4057
  }
4058

4059
  /**
4060
   * Fulfill a requested block.
4061
   * @param {Peer} peer
4062
   * @param {Hash} hash
4063
   * @returns {Boolean}
4064
   */
4065

4066
  resolveBlock(peer, hash) {
4067
    if (!peer.blockMap.has(hash))
894!
4068
      return false;
×
4069

4070
    peer.blockMap.delete(hash);
894✔
4071

4072
    assert(this.blockMap.has(hash));
894✔
4073
    this.blockMap.delete(hash);
894✔
4074

4075
    return true;
894✔
4076
  }
4077

4078
  /**
4079
   * Fulfill a requested claim.
4080
   * @param {Peer} peer
4081
   * @param {Hash} hash
4082
   * @returns {Boolean}
4083
   */
4084

4085
  resolveClaim(peer, hash) {
4086
    if (!peer.claimMap.has(hash))
×
4087
      return false;
×
4088

4089
    peer.claimMap.delete(hash);
×
4090

4091
    assert(this.claimMap.has(hash));
×
4092
    this.claimMap.delete(hash);
×
4093

4094
    return true;
×
4095
  }
4096

4097
  /**
4098
   * Fulfill a requested claim.
4099
   * @param {Peer} peer
4100
   * @param {Hash} hash
4101
   * @returns {Boolean}
4102
   */
4103

4104
  resolveAirdrop(peer, hash) {
4105
    if (!peer.airdropMap.has(hash))
×
4106
      return false;
×
4107

4108
    peer.airdropMap.delete(hash);
×
4109

4110
    assert(this.airdropMap.has(hash));
×
4111
    this.airdropMap.delete(hash);
×
4112

4113
    return true;
×
4114
  }
4115

4116
  /**
4117
   * Fulfill a requested item.
4118
   * @param {Peer} peer
4119
   * @param {InvItem} item
4120
   * @returns {Boolean}
4121
   */
4122

4123
  resolveItem(peer, item) {
4124
    if (item.isBlock())
41!
4125
      return this.resolveBlock(peer, item.hash);
×
4126

4127
    if (item.isTX())
41!
4128
      return this.resolveTX(peer, item.hash);
41✔
4129

4130
    if (item.isClaim())
×
4131
      return this.resolveClaim(peer, item.hash);
×
4132

4133
    if (item.isAirdrop())
×
4134
      return this.resolveAirdrop(peer, item.hash);
×
4135

4136
    return false;
×
4137
  }
4138

4139
  /**
4140
   * Broadcast a transaction, block, or claim.
4141
   * @param {TX|Block|Claim|AirdropProof} msg
4142
   * @returns {Promise}
4143
   */
4144

4145
  broadcast(msg) {
4146
    const hash = msg.hash();
2✔
4147

4148
    let item = this.invMap.get(hash);
2✔
4149

4150
    if (item) {
2!
4151
      item.refresh();
×
4152
      item.announce();
×
4153
    } else {
4154
      item = new BroadcastItem(this, msg);
2✔
4155
      item.start();
2✔
4156
      item.announce();
2✔
4157
    }
4158

4159
    return new Promise((resolve, reject) => {
2✔
4160
      item.addJob(resolve, reject);
2✔
4161
    });
4162
  }
4163

4164
  /**
4165
   * Announce a block to all peers.
4166
   * @param {Block} msg
4167
   */
4168

4169
  announceBlock(msg) {
4170
    for (let peer = this.peers.head(); peer; peer = peer.next)
8,329✔
4171
      peer.announceBlock(msg);
966✔
4172
  }
4173

4174
  /**
4175
   * Announce a transaction to all peers.
4176
   * @param {TX} msg
4177
   */
4178

4179
  announceTX(msg) {
4180
    for (let peer = this.peers.head(); peer; peer = peer.next)
1,104✔
4181
      peer.announceTX(msg);
62✔
4182
  }
4183

4184
  /**
4185
   * Announce a transaction to all peers.
4186
   * @param {Claim} msg
4187
   */
4188

4189
  announceClaim(msg) {
4190
    for (let peer = this.peers.head(); peer; peer = peer.next)
19✔
4191
      peer.announceClaim(msg);
×
4192
  }
4193

4194
  /**
4195
   * Announce a transaction to all peers.
4196
   * @param {AirdropProof} msg
4197
   */
4198

4199
  announceAirdrop(msg) {
4200
    for (let peer = this.peers.head(); peer; peer = peer.next)
3✔
4201
      peer.announceAirdrop(msg);
×
4202
  }
4203

4204
  /**
4205
   * Returns human readable list of services
4206
   * that are available.
4207
   * @returns {String[]}
4208
   */
4209

4210
  getServiceNames() {
4211
    const enabled = [];
1✔
4212

4213
    for (const [service, bit] of Object.entries(services)) {
1✔
4214
      if (this.options.hasServices(bit))
2!
4215
        enabled.push(service);
2✔
4216
    }
4217

4218
    return enabled;
1✔
4219
  }
4220

4221
  /**
4222
   * Pick prover for name proofs.
4223
   * @param {Buffer} nameHash
4224
   * @returns {Buffer}
4225
   */
4226

4227
  pickProver(nameHash) {
4228
    let firstBest = this.peers.head();
54✔
4229
    let secondBest = null;
54✔
4230
    let deterministic = null;
54✔
4231
    let rand = null;
54✔
4232
    let total = 0;
54✔
4233
    let peer;
4234

4235
    for (peer = this.peers.head(); peer; peer = peer.next) {
54✔
4236
      if (!peer.ack || !(peer.services & common.services.NETWORK))
54!
4237
        continue;
×
4238

4239
      if (peer.totalProofs > firstBest.totalProofs
54!
4240
          && peer.nameMap.size <= firstBest.nameMap.size) {
4241
        secondBest = firstBest;
×
4242
        firstBest = peer;
×
4243
      }
4244

4245
      total += 1;
54✔
4246
    }
4247

4248
    if (total === 0)
54!
4249
      return null;
×
4250

4251
    let i = nameHash[0] % total;
54✔
4252
    let r = random(total);
54✔
4253

4254
    for (peer = this.peers.head(); peer; peer = peer.next) {
54✔
4255
      if (!peer.ack || !(peer.services & common.services.NETWORK))
54!
4256
        continue;
×
4257

4258
      if (i === 0)
54!
4259
        deterministic = peer;
54✔
4260

4261
      if (r === 0)
54!
4262
        rand = peer;
54✔
4263

4264
      i -= 1;
54✔
4265
      r -= 1;
54✔
4266
    }
4267

4268
    if (rand && random(5) === 0)
54✔
4269
      return rand;
9✔
4270

4271
    if (secondBest && random(10) === 0)
45!
4272
      return secondBest;
×
4273

4274
    if (firstBest && random(10) === 0)
45✔
4275
      return firstBest;
3✔
4276

4277
    return deterministic;
42✔
4278
  }
4279

4280
  /**
4281
   * Resolve a name at the "safe" Urkel Tree root.
4282
   * @param {Buffer} nameHash
4283
   * @returns {Buffer}
4284
   */
4285

4286
   async resolve(nameHash) {
4287
    const root = await this.chain.getSafeRoot();
51✔
4288
    return this.resolveAtRoot(nameHash, root);
51✔
4289
   }
4290

4291
  /**
4292
   * Resolve a name given any Urkel Tree root.
4293
   * @param {Buffer} nameHash
4294
   * @param {Buffer} root
4295
   * @returns {Buffer}
4296
   */
4297

4298
  async resolveAtRoot(nameHash, root) {
4299
    assert(Buffer.isBuffer(nameHash));
54✔
4300
    assert(Buffer.isBuffer(root));
54✔
4301

4302
    if (!this.chain.synced)
54!
4303
      throw new Error('Chain is not synced.');
×
4304

4305
    const existing = this.nameMap.get(nameHash);
54✔
4306

4307
    if (existing) {
54!
4308
      return new Promise((resolve, reject) => {
×
4309
        existing.addJob(resolve, reject);
×
4310
      });
4311
    }
4312

4313
    const peer = this.pickProver(nameHash);
54✔
4314

4315
    if (!peer)
54!
4316
      throw new Error('No peers available.');
×
4317

4318
    const item = new NameRequest(root);
54✔
4319

4320
    this.nameMap.set(nameHash, item);
54✔
4321
    peer.nameMap.set(nameHash, Date.now());
54✔
4322
    peer.sendGetProof(root, nameHash);
54✔
4323

4324
    return new Promise((resolve, reject) => {
54✔
4325
      item.addJob(resolve, reject);
54✔
4326
    });
4327
  }
4328
}
4329

4330
/**
4331
 * Interval for refilling outbound peers.
4332
 * @const {Number}
4333
 * @default
4334
 */
4335

4336
Pool.REFILL_INTERVAL = 3000;
1✔
4337

4338
/**
4339
 * Discovery interval for UPNP and DNS seeds.
4340
 * @const {Number}
4341
 * @default
4342
 */
4343

4344
Pool.DISCOVERY_INTERVAL = 120000;
1✔
4345

4346
/**
4347
 * Pool Options
4348
 * @alias module:net.PoolOptions
4349
 */
4350

4351
class PoolOptions {
4352
  /**
4353
   * Create pool options.
4354
   * @constructor
4355
   */
4356

4357
  constructor(options) {
4358
    this.network = Network.primary;
178✔
4359
    this.logger = null;
178✔
4360
    this.chain = null;
178✔
4361
    this.mempool = null;
178✔
4362

4363
    this.nonces = new NonceList();
178✔
4364

4365
    this.prefix = null;
178✔
4366
    this.checkpoints = true;
178✔
4367
    this.spv = false;
178✔
4368
    this.bip37 = false;
178✔
4369
    this.listen = false;
178✔
4370
    this.compact = true;
178✔
4371
    this.noRelay = false;
178✔
4372
    this.host = '0.0.0.0';
178✔
4373
    this.port = this.network.port;
178✔
4374
    this.brontidePort = this.network.brontidePort;
178✔
4375
    this.publicHost = '0.0.0.0';
178✔
4376
    this.publicPort = this.network.port;
178✔
4377
    this.publicBrontidePort = this.network.brontidePort;
178✔
4378
    this.maxOutbound = 8;
178✔
4379
    this.maxInbound = 20;
178✔
4380
    this.createSocket = this._createSocket.bind(this);
178✔
4381
    this.createServer = tcp.createServer;
178✔
4382
    this.resolve = this._resolve.bind(this);
178✔
4383
    this.createNonce = this._createNonce.bind(this);
178✔
4384
    this.hasNonce = this._hasNonce.bind(this);
178✔
4385
    this.getHeight = this._getHeight.bind(this);
178✔
4386
    this.isFull = this._isFull.bind(this);
178✔
4387
    this.getRate = this._getRate.bind(this);
178✔
4388
    this.proxy = null;
178✔
4389
    this.onion = false;
178✔
4390
    this.brontideOnly = false;
178✔
4391
    this.upnp = false;
178✔
4392
    this.version = common.PROTOCOL_VERSION;
178✔
4393
    this.agent = common.USER_AGENT;
178✔
4394
    this.identityKey = secp256k1.privateKeyGenerate();
178✔
4395
    this.banScore = common.BAN_SCORE;
178✔
4396
    this.banTime = common.BAN_TIME;
178✔
4397
    this.maxProofRPS = 100;
178✔
4398
    this.feeRate = -1;
178✔
4399
    this.seeds = this.network.seeds;
178✔
4400
    this.nodes = [];
178✔
4401
    this.invTimeout = 60000;
178✔
4402
    this.blockMode = 0;
178✔
4403
    this.services = common.LOCAL_SERVICES;
178✔
4404
    this.requiredServices = common.REQUIRED_SERVICES;
178✔
4405
    this.memory = true;
178✔
4406

4407
    this.fromOptions(options);
178✔
4408
  }
4409

4410
  /**
4411
   * Inject properties from object.
4412
   * @private
4413
   * @param {Object} options
4414
   * @returns {PoolOptions}
4415
   */
4416

4417
  fromOptions(options) {
4418
    assert(options, 'Pool requires options.');
178✔
4419
    assert(options.chain && typeof options.chain === 'object',
178✔
4420
      'Pool options require a blockchain.');
4421

4422
    this.chain = options.chain;
178✔
4423
    this.network = options.chain.network;
178✔
4424
    this.logger = options.chain.logger;
178✔
4425

4426
    this.port = this.network.port;
178✔
4427
    this.seeds = this.network.seeds;
178✔
4428
    this.port = this.network.port;
178✔
4429
    this.brontidePort = this.network.brontidePort;
178✔
4430
    this.publicPort = this.network.port;
178✔
4431
    this.publicBrontidePort = this.network.brontidePort;
178✔
4432

4433
    if (options.logger != null) {
178✔
4434
      assert(typeof options.logger === 'object');
174✔
4435
      this.logger = options.logger;
174✔
4436
    }
4437

4438
    if (options.mempool != null) {
178✔
4439
      assert(typeof options.mempool === 'object');
159✔
4440
      this.mempool = options.mempool;
159✔
4441
    }
4442

4443
    if (options.prefix != null) {
178✔
4444
      assert(typeof options.prefix === 'string');
174✔
4445
      this.prefix = options.prefix;
174✔
4446
    }
4447

4448
    if (options.checkpoints != null) {
178!
4449
      assert(typeof options.checkpoints === 'boolean');
×
4450
      assert(options.checkpoints === this.chain.options.checkpoints);
×
4451
      this.checkpoints = options.checkpoints;
×
4452
    } else {
4453
      this.checkpoints = this.chain.options.checkpoints;
178✔
4454
    }
4455

4456
    if (options.spv != null) {
178!
4457
      assert(typeof options.spv === 'boolean');
×
4458
      assert(options.spv === this.chain.options.spv);
×
4459
      this.spv = options.spv;
×
4460
    } else {
4461
      this.spv = this.chain.options.spv;
178✔
4462
    }
4463

4464
    if (options.bip37 != null) {
178✔
4465
      assert(typeof options.bip37 === 'boolean');
8✔
4466
      this.bip37 = options.bip37;
8✔
4467
    }
4468

4469
    if (options.listen != null) {
178✔
4470
      assert(typeof options.listen === 'boolean');
111✔
4471
      this.listen = options.listen;
111✔
4472
    }
4473

4474
    if (options.compact != null) {
178!
4475
      assert(typeof options.compact === 'boolean');
×
4476
      this.compact = options.compact;
×
4477
    }
4478

4479
    if (options.noRelay != null) {
178!
4480
      assert(typeof options.noRelay === 'boolean');
×
4481
      this.noRelay = options.noRelay;
×
4482
    }
4483

4484
    if (options.host != null)
178!
4485
      this.host = IP.normalize(options.host);
×
4486

4487
    if (options.port != null) {
178✔
4488
      assert((options.port & 0xffff) === options.port);
58✔
4489
      this.port = options.port;
58✔
4490
    }
4491

4492
    if (options.brontidePort != null) {
178✔
4493
      assert((options.brontidePort & 0xffff) === options.brontidePort);
41✔
4494
      this.brontidePort = options.brontidePort;
41✔
4495
    }
4496

4497
    if (options.publicHost != null) {
178✔
4498
      const raw = IP.toBuffer(options.publicHost);
3✔
4499

4500
      if (!IP.isRoutable(raw))
3!
4501
        throw new Error('Invalid public host.');
×
4502

4503
      this.publicHost = IP.toString(raw);
3✔
4504
    }
4505

4506
    if (options.publicPort != null) {
178✔
4507
      assert((options.publicPort & 0xffff) === options.publicPort);
2✔
4508
      this.publicPort = options.publicPort;
2✔
4509
    }
4510

4511
    if (options.publicBrontidePort != null) {
178✔
4512
      assert((options.publicBrontidePort & 0xffff)
1✔
4513
             === options.publicBrontidePort);
4514
      this.publicBrontidePort = options.publicBrontidePort;
1✔
4515
    }
4516

4517
    if (options.maxOutbound != null) {
178✔
4518
      assert(typeof options.maxOutbound === 'number');
2✔
4519
      assert(options.maxOutbound > 0);
2✔
4520
      this.maxOutbound = options.maxOutbound;
2✔
4521
    }
4522

4523
    if (options.maxInbound != null) {
178!
4524
      assert(typeof options.maxInbound === 'number');
×
4525
      this.maxInbound = options.maxInbound;
×
4526
    }
4527

4528
    if (options.createSocket) {
178!
4529
      assert(typeof options.createSocket === 'function');
×
4530
      this.createSocket = options.createSocket;
×
4531
    }
4532

4533
    if (options.createServer) {
178!
4534
      assert(typeof options.createServer === 'function');
×
4535
      this.createServer = options.createServer;
×
4536
    }
4537

4538
    if (options.resolve) {
178!
4539
      assert(typeof options.resolve === 'function');
×
4540
      this.resolve = options.resolve;
×
4541
    }
4542

4543
    if (options.proxy) {
178!
4544
      assert(typeof options.proxy === 'string');
×
4545
      this.proxy = options.proxy;
×
4546
    }
4547

4548
    if (options.onion != null) {
178!
4549
      assert(typeof options.onion === 'boolean');
×
4550
      this.onion = options.onion;
×
4551
    }
4552

4553
    if (options.brontideOnly != null) {
178!
4554
      assert(typeof options.brontideOnly === 'boolean');
×
4555
      this.brontideOnly = options.brontideOnly;
×
4556
    }
4557

4558
    if (options.upnp != null) {
178!
4559
      assert(typeof options.upnp === 'boolean');
×
4560
      this.upnp = options.upnp;
×
4561
    }
4562

4563
    if (options.version) {
178!
4564
      assert(typeof options.version === 'number');
×
4565
      this.version = options.version;
×
4566
    }
4567

4568
    if (options.agent) {
178✔
4569
      assert(typeof options.agent === 'string');
3✔
4570
      assert(!options.agent.includes('/'), 'User agent can not include /');
3✔
4571
      this.agent += `${options.agent}/`;
2✔
4572

4573
      assert(this.agent.length <= 255, 'User agent exceeds maximum length');
2✔
4574
    }
4575

4576
    if (options.identityKey) {
176✔
4577
      assert(Buffer.isBuffer(options.identityKey),
174✔
4578
        'Identity key must be a buffer.');
4579
      assert(secp256k1.privateKeyVerify(options.identityKey),
174✔
4580
        'Invalid identity key.');
4581
      this.identityKey = options.identityKey;
174✔
4582
    }
4583

4584
    if (options.banScore != null) {
176!
4585
      assert(typeof this.options.banScore === 'number');
×
4586
      this.banScore = this.options.banScore;
×
4587
    }
4588

4589
    if (options.banTime != null) {
176!
4590
      assert(typeof this.options.banTime === 'number');
×
4591
      this.banTime = this.options.banTime;
×
4592
    }
4593

4594
    if (options.maxProofRPS != null) {
176✔
4595
      assert(typeof options.maxProofRPS === 'number');
2✔
4596
      this.maxProofRPS = options.maxProofRPS;
2✔
4597
    }
4598

4599
    if (options.feeRate != null) {
176!
4600
      assert(typeof this.options.feeRate === 'number');
×
4601
      this.feeRate = this.options.feeRate;
×
4602
    }
4603

4604
    if (options.seeds) {
176✔
4605
      assert(Array.isArray(options.seeds));
49✔
4606
      this.seeds = options.seeds;
49✔
4607
    }
4608

4609
    if (options.nodes) {
176✔
4610
      assert(Array.isArray(options.nodes));
1✔
4611
      this.nodes = options.nodes;
1✔
4612
    }
4613

4614
    if (options.only != null) {
176✔
4615
      assert(Array.isArray(options.only));
12✔
4616
      if (options.only.length > 0) {
12!
4617
        this.nodes = options.only;
12✔
4618
        this.maxOutbound = options.only.length;
12✔
4619
      }
4620
    }
4621

4622
    if (options.invTimeout != null) {
176!
4623
      assert(typeof options.invTimeout === 'number');
×
4624
      this.invTimeout = options.invTimeout;
×
4625
    }
4626

4627
    if (options.blockMode != null) {
176!
4628
      assert(typeof options.blockMode === 'number');
×
4629
      this.blockMode = options.blockMode;
×
4630
    }
4631

4632
    if (options.memory != null) {
176✔
4633
      assert(typeof options.memory === 'boolean');
171✔
4634
      this.memory = options.memory;
171✔
4635
    }
4636

4637
    if (this.spv) {
176✔
4638
      this.requiredServices |= common.services.BLOOM;
15✔
4639
      this.services &= ~common.services.NETWORK;
15✔
4640
      this.noRelay = true;
15✔
4641
      this.checkpoints = true;
15✔
4642
      this.compact = false;
15✔
4643
      this.bip37 = false;
15✔
4644
      this.listen = false;
15✔
4645
    }
4646

4647
    if (this.bip37)
176✔
4648
      this.services |= common.services.BLOOM;
8✔
4649

4650
    if (this.proxy)
176!
4651
      this.listen = false;
×
4652

4653
    if (options.services != null) {
176!
4654
      assert((options.services >>> 0) === options.services);
×
4655
      this.services = options.services;
×
4656
    }
4657

4658
    if (options.requiredServices != null) {
176!
4659
      assert((options.requiredServices >>> 0) === options.requiredServices);
×
4660
      this.requiredServices = options.requiredServices;
×
4661
    }
4662

4663
    return this;
176✔
4664
  }
4665

4666
  /**
4667
   * Instantiate options from object.
4668
   * @param {Object} options
4669
   * @returns {PoolOptions}
4670
   */
4671

4672
  static fromOptions(options) {
4673
    return new PoolOptions().fromOptions(options);
×
4674
  }
4675

4676
  /**
4677
   * Get the chain height.
4678
   * @private
4679
   * @returns {Number}
4680
   */
4681

4682
  _getHeight() {
4683
    return this.chain.height;
92✔
4684
  }
4685

4686
  /**
4687
   * Test whether the chain is synced.
4688
   * @private
4689
   * @returns {Boolean}
4690
   */
4691

4692
  _isFull() {
4693
    return this.chain.synced;
59✔
4694
  }
4695

4696
  /**
4697
   * Get required services for outbound peers.
4698
   * @private
4699
   * @returns {Number}
4700
   */
4701

4702
  getRequiredServices() {
4703
    return this.requiredServices;
160✔
4704
  }
4705

4706
  /**
4707
   * Test whether required services are available.
4708
   * @param {Number} services
4709
   * @returns {Boolean}
4710
   */
4711

4712
  hasServices(services) {
4713
    return (this.services & services) === services;
2✔
4714
  }
4715

4716
  /**
4717
   * Create a version packet nonce.
4718
   * @private
4719
   * @param {String} hostname
4720
   * @returns {Buffer}
4721
   */
4722

4723
  _createNonce(hostname) {
4724
    return this.nonces.alloc(hostname);
92✔
4725
  }
4726

4727
  /**
4728
   * Test whether version nonce is ours.
4729
   * @private
4730
   * @param {Buffer} nonce
4731
   * @returns {Boolean}
4732
   */
4733

4734
  _hasNonce(nonce) {
4735
    return this.nonces.has(nonce);
8✔
4736
  }
4737

4738
  /**
4739
   * Get fee rate for txid.
4740
   * @private
4741
   * @param {Hash} hash
4742
   * @returns {Rate}
4743
   */
4744

4745
  _getRate(hash) {
4746
    if (!this.mempool)
×
4747
      return -1;
×
4748

4749
    const entry = this.mempool.getEntry(hash);
×
4750

4751
    if (!entry)
×
4752
      return -1;
×
4753

4754
    return entry.getRate();
×
4755
  }
4756

4757
  /**
4758
   * Default createSocket call.
4759
   * @private
4760
   * @param {Number} port
4761
   * @param {String} host
4762
   * @returns {net.Socket}
4763
   */
4764

4765
  _createSocket(port, host) {
4766
    if (this.proxy)
75!
4767
      return socks.connect(this.proxy, port, host);
×
4768

4769
    return tcp.createSocket(port, host);
75✔
4770
  }
4771

4772
  /**
4773
   * Default resolve call.
4774
   * @private
4775
   * @param {String} name
4776
   * @returns {String[]}
4777
   */
4778

4779
  _resolve(name) {
4780
    if (this.onion)
×
4781
      return socks.resolve(this.proxy, name);
×
4782

4783
    return lookup(name);
×
4784
  }
4785
}
4786

4787
/**
4788
 * Peer List
4789
 * @alias module:net.PeerList
4790
 */
4791

4792
class PeerList {
4793
  /**
4794
   * Create peer list.
4795
   * @constructor
4796
   * @param {Object} options
4797
   */
4798

4799
  constructor() {
4800
    this.map = new Map();
176✔
4801
    this.ids = new Map();
176✔
4802
    this.list = new List();
176✔
4803
    this.load = null;
176✔
4804
    this.inbound = 0;
176✔
4805
    this.outbound = 0;
176✔
4806
  }
4807

4808
  /**
4809
   * Get the list head.
4810
   * @returns {Peer}
4811
   */
4812

4813
  head() {
4814
    return this.list.head;
10,736✔
4815
  }
4816

4817
  /**
4818
   * Get the list tail.
4819
   * @returns {Peer}
4820
   */
4821

4822
  tail() {
4823
    return this.list.tail;
×
4824
  }
4825

4826
  /**
4827
   * Get list size.
4828
   * @returns {Number}
4829
   */
4830

4831
  size() {
4832
    return this.list.size;
165✔
4833
  }
4834

4835
  /**
4836
   * Add peer to list.
4837
   * @param {Peer} peer
4838
   */
4839

4840
  add(peer) {
4841
    assert(this.list.push(peer));
120✔
4842

4843
    assert(!this.map.has(peer.hostname()));
120✔
4844
    this.map.set(peer.hostname(), peer);
120✔
4845

4846
    assert(!this.ids.has(peer.id));
120✔
4847
    this.ids.set(peer.id, peer);
120✔
4848

4849
    if (peer.outbound)
120✔
4850
      this.outbound += 1;
75✔
4851
    else
4852
      this.inbound += 1;
45✔
4853
  }
4854

4855
  /**
4856
   * Remove peer from list.
4857
   * @param {Peer} peer
4858
   */
4859

4860
  remove(peer) {
4861
    assert(this.list.remove(peer));
120✔
4862

4863
    assert(this.ids.has(peer.id));
120✔
4864
    this.ids.delete(peer.id);
120✔
4865

4866
    assert(this.map.has(peer.hostname()));
120✔
4867
    this.map.delete(peer.hostname());
120✔
4868

4869
    if (peer === this.load) {
120✔
4870
      assert(peer.loader);
52✔
4871
      peer.loader = false;
52✔
4872
      this.load = null;
52✔
4873
    }
4874

4875
    if (peer.outbound)
120✔
4876
      this.outbound -= 1;
75✔
4877
    else
4878
      this.inbound -= 1;
45✔
4879
  }
4880

4881
  /**
4882
   * Get peer by hostname.
4883
   * @param {String} hostname
4884
   * @returns {Peer}
4885
   */
4886

4887
  get(hostname) {
4888
    return this.map.get(hostname);
1✔
4889
  }
4890

4891
  /**
4892
   * Test whether a peer exists.
4893
   * @param {String} hostname
4894
   * @returns {Boolean}
4895
   */
4896

4897
  has(hostname) {
4898
    return this.map.has(hostname);
117✔
4899
  }
4900

4901
  /**
4902
   * Get peer by ID.
4903
   * @param {Number} id
4904
   * @returns {Peer}
4905
   */
4906

4907
  find(id) {
4908
    return this.ids.get(id);
120✔
4909
  }
4910

4911
  /**
4912
   * Destroy peer list (kills peers).
4913
   */
4914

4915
  destroy() {
4916
    let next;
4917

4918
    for (let peer = this.list.head; peer; peer = next) {
110✔
4919
      next = peer.next;
77✔
4920
      peer.destroy();
77✔
4921
    }
4922
  }
4923
}
4924

4925
/**
4926
 * Broadcast Item
4927
 * Represents an item that is broadcasted via an inv/getdata cycle.
4928
 * @alias module:net.BroadcastItem
4929
 * @extends EventEmitter
4930
 * @private
4931
 * @emits BroadcastItem#ack
4932
 * @emits BroadcastItem#reject
4933
 * @emits BroadcastItem#timeout
4934
 */
4935

4936
class BroadcastItem extends EventEmitter {
4937
  /**
4938
   * Create broadcast item.
4939
   * @constructor
4940
   * @param {Pool} pool
4941
   * @param {TX|Block|Claim|AirdropProof} msg
4942
   */
4943

4944
  constructor(pool, msg) {
4945
    super();
2✔
4946

4947
    assert(!msg.mutable, 'Cannot broadcast mutable item.');
2✔
4948

4949
    const item = msg.toInv();
2✔
4950

4951
    this.pool = pool;
2✔
4952
    this.hash = item.hash;
2✔
4953
    this.type = item.type;
2✔
4954
    this.msg = msg;
2✔
4955
    this.jobs = [];
2✔
4956
  }
4957

4958
  /**
4959
   * Add a job to be executed on ack, timeout, or reject.
4960
   * @returns {Promise}
4961
   */
4962

4963
  addJob(resolve, reject) {
4964
    this.jobs.push({ resolve, reject });
2✔
4965
  }
4966

4967
  /**
4968
   * Start the broadcast.
4969
   */
4970

4971
  start() {
4972
    assert(!this.timeout, 'Already started.');
2✔
4973
    assert(!this.pool.invMap.has(this.hash), 'Already started.');
2✔
4974

4975
    this.pool.invMap.set(this.hash, this);
2✔
4976

4977
    this.refresh();
2✔
4978

4979
    return this;
2✔
4980
  }
4981

4982
  /**
4983
   * Refresh the timeout on the broadcast.
4984
   */
4985

4986
  refresh() {
4987
    if (this.timeout != null) {
2!
4988
      clearTimeout(this.timeout);
×
4989
      this.timeout = null;
×
4990
    }
4991

4992
    this.timeout = setTimeout(() => {
2✔
4993
      this.emit('timeout');
×
4994
      this.reject(new Error('Timed out.'));
×
4995
    }, this.pool.options.invTimeout);
4996
  }
4997

4998
  /**
4999
   * Announce the item.
5000
   */
5001

5002
  announce() {
5003
    switch (this.type) {
2!
5004
      case invTypes.TX:
5005
        this.pool.announceTX(this.msg);
1✔
5006
        break;
1✔
5007
      case invTypes.BLOCK:
5008
        this.pool.announceBlock(this.msg);
×
5009
        break;
×
5010
      case invTypes.CLAIM:
5011
        this.pool.announceClaim(this.msg);
1✔
5012
        break;
1✔
5013
      case invTypes.AIRDROP:
5014
        this.pool.announceAirdrop(this.msg);
×
5015
        break;
×
5016
      default:
5017
        assert(false, 'Bad type.');
×
5018
        break;
×
5019
    }
5020
  }
5021

5022
  /**
5023
   * Finish the broadcast.
5024
   */
5025

5026
  cleanup() {
5027
    assert(this.timeout != null, 'Already finished.');
2✔
5028
    assert(this.pool.invMap.has(this.hash), 'Already finished.');
2✔
5029

5030
    clearTimeout(this.timeout);
2✔
5031
    this.timeout = null;
2✔
5032

5033
    this.pool.invMap.delete(this.hash);
2✔
5034
  }
5035

5036
  /**
5037
   * Finish the broadcast, return with an error.
5038
   * @param {Error} err
5039
   */
5040

5041
  reject(err) {
5042
    this.cleanup();
×
5043

5044
    for (const job of this.jobs)
×
5045
      job.reject(err);
×
5046

5047
    this.jobs.length = 0;
×
5048
  }
5049

5050
  /**
5051
   * Finish the broadcast successfully.
5052
   */
5053

5054
  resolve() {
5055
    this.cleanup();
2✔
5056

5057
    for (const job of this.jobs)
2✔
5058
      job.resolve(false);
2✔
5059

5060
    this.jobs.length = 0;
2✔
5061
  }
5062

5063
  /**
5064
   * Handle an ack from a peer.
5065
   * @param {Peer} peer
5066
   */
5067

5068
  handleAck(peer) {
5069
    setTimeout(() => {
×
5070
      this.emit('ack', peer);
×
5071

5072
      for (const job of this.jobs)
×
5073
        job.resolve(true);
×
5074

5075
      this.jobs.length = 0;
×
5076
    }, 1000);
5077
  }
5078

5079
  /**
5080
   * Handle a reject from a peer.
5081
   * @param {Peer} peer
5082
   */
5083

5084
  handleReject(peer) {
5085
    this.emit('reject', peer);
×
5086

5087
    for (const job of this.jobs)
×
5088
      job.resolve(false);
×
5089

5090
    this.jobs.length = 0;
×
5091
  }
5092

5093
  /**
5094
   * Inspect the broadcast item.
5095
   * @returns {String}
5096
   */
5097

5098
  inspect() {
5099
    const hash = this.hash;
×
5100

5101
    let name = '';
×
5102

5103
    switch (this.type) {
×
5104
      case invTypes.TX:
5105
        name = 'tx';
×
5106
        break;
×
5107
      case invTypes.BLOCK:
5108
        name = 'block';
×
5109
        break;
×
5110
      case invTypes.CLAIM:
5111
        name = 'claim';
×
5112
        break;
×
5113
      case invTypes.AIRDROP:
5114
        name = 'airdrop';
×
5115
        break;
×
5116
    }
5117

5118
    return `<BroadcastItem: type=${name} hash=${hash.toString('hex')}>`;
×
5119
  }
5120
}
5121

5122
/**
5123
 * Nonce List
5124
 * @ignore
5125
 */
5126

5127
class NonceList {
5128
  /**
5129
   * Create nonce list.
5130
   * @constructor
5131
   */
5132

5133
  constructor() {
5134
    this.map = new BufferMap();
178✔
5135
    this.hosts = new Map();
178✔
5136
  }
5137

5138
  alloc(hostname) {
5139
    for (;;) {
92✔
5140
      const nonce = common.nonce();
92✔
5141

5142
      if (this.map.has(nonce))
92!
5143
        continue;
×
5144

5145
      this.map.set(nonce, hostname);
92✔
5146

5147
      assert(!this.hosts.has(hostname));
92✔
5148
      this.hosts.set(hostname, nonce);
92✔
5149

5150
      return nonce;
92✔
5151
    }
5152
  }
5153

5154
  has(nonce) {
5155
    return this.map.has(nonce);
8✔
5156
  }
5157

5158
  remove(hostname) {
5159
    const nonce = this.hosts.get(hostname);
210✔
5160

5161
    if (!nonce)
210✔
5162
      return false;
118✔
5163

5164
    this.hosts.delete(hostname);
92✔
5165

5166
    assert(this.map.has(nonce));
92✔
5167
    this.map.delete(nonce);
92✔
5168

5169
    return true;
92✔
5170
  }
5171
}
5172

5173
/**
5174
 * Header Entry
5175
 * @ignore
5176
 */
5177

5178
class HeaderEntry {
5179
  /**
5180
   * Create header entry.
5181
   * @constructor
5182
   */
5183

5184
  constructor(hash, height) {
5185
    this.hash = hash;
44✔
5186
    this.height = height;
44✔
5187
    this.prev = null;
44✔
5188
    this.next = null;
44✔
5189
  }
5190
}
5191

5192
/**
5193
 * Name Request
5194
 * @ignore
5195
 */
5196

5197
class NameRequest {
5198
  /**
5199
   * Create name request.
5200
   * @constructor
5201
   */
5202

5203
  constructor(root) {
5204
    this.root = root;
54✔
5205
    this.jobs = [];
54✔
5206
  }
5207

5208
  /**
5209
   * Add a job to be executed on ack, timeout, or reject.
5210
   * @returns {Promise}
5211
   */
5212

5213
  addJob(resolve, reject) {
5214
    this.jobs.push({ resolve, reject });
54✔
5215
  }
5216

5217
  /**
5218
   * Finish the broadcast, return with an error.
5219
   * @param {Error} err
5220
   */
5221

5222
  reject(err) {
5223
    for (const job of this.jobs)
2✔
5224
      job.reject(err);
2✔
5225

5226
    this.jobs.length = 0;
2✔
5227
  }
5228

5229
  /**
5230
   * Finish the broadcast successfully.
5231
   */
5232

5233
  resolve(result) {
5234
    for (const job of this.jobs)
52✔
5235
      job.resolve(result);
52✔
5236

5237
    this.jobs.length = 0;
52✔
5238
  }
5239
}
5240

5241
/*
5242
 * Helpers
5243
 */
5244

5245
function random(max) {
5246
  return rng.randomRange(0, max);
153✔
5247
}
5248

5249
/*
5250
 * Expose
5251
 */
5252

5253
module.exports = Pool;
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