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

handshake-org / hsd / 12515507044

27 Dec 2024 11:26AM UTC coverage: 71.242% (-0.02%) from 71.265%
12515507044

push

github

nodech
Merge PR #914 from 'nodech/add-mid-checkpoint'

8053 of 13154 branches covered (61.22%)

Branch coverage included in aggregate %.

25712 of 34241 relevant lines covered (75.09%)

34514.12 hits per line

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

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

7
'use strict';
8

9
const assert = require('bsert');
1✔
10
const EventEmitter = require('events');
1✔
11
const {Lock} = require('bmutex');
1✔
12
const {format} = require('util');
1✔
13
const tcp = require('btcp');
1✔
14
const dns = require('bdns');
1✔
15
const Logger = require('blgr');
1✔
16
const {RollingFilter} = require('@handshake-org/bfilter');
1✔
17
const {BufferMap} = require('buffer-map');
1✔
18
const Parser = require('./parser');
1✔
19
const Framer = require('./framer');
1✔
20
const packets = require('./packets');
1✔
21
const consensus = require('../protocol/consensus');
1✔
22
const common = require('./common');
1✔
23
const InvItem = require('../primitives/invitem');
1✔
24
const BIP152 = require('./bip152');
1✔
25
const Block = require('../primitives/block');
1✔
26
const TX = require('../primitives/tx');
1✔
27
const Claim = require('../primitives/claim');
1✔
28
const NetAddress = require('./netaddress');
1✔
29
const Network = require('../protocol/network');
1✔
30
const {BrontideStream} = require('./brontide');
1✔
31
const AirdropProof = require('../primitives/airdropproof');
1✔
32
const SlidingWindow = require('./slidingwindow');
1✔
33
const services = common.services;
1✔
34
const invTypes = InvItem.types;
1✔
35
const packetTypes = packets.types;
1✔
36

37
/** @typedef {import('net').Socket} NetSocket */
38
/** @typedef {import('../types').Hash} Hash */
39
/** @typedef {import('../types').Rate} Rate */
40
/** @typedef {import('../protocol/errors').VerifyError} VerifyError */
41

42
/**
43
 * Represents a network peer.
44
 * @alias module:net.Peer
45
 * @extends EventEmitter
46
 */
47

48
class Peer extends EventEmitter {
49
  /**
50
   * Create a peer.
51
   * @alias module:net.Peer
52
   * @constructor
53
   * @param {PeerOptions} options
54
   */
55

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

59
    this.options = new PeerOptions(options);
120✔
60
    this.network = this.options.network;
120✔
61
    this.logger = this.options.logger.context('peer');
120✔
62
    this.locker = new Lock();
120✔
63

64
    this.parser = new Parser(this.network);
120✔
65
    this.framer = new Framer(this.network);
120✔
66

67
    this.id = -1;
120✔
68
    this.stream = null;
120✔
69
    this.socket = null;
120✔
70
    this.brontide = new BrontideStream();
120✔
71
    this.encrypted = false;
120✔
72
    this.identityKey = this.options.identityKey;
120✔
73
    this.opened = false;
120✔
74
    this.outbound = false;
120✔
75
    this.loader = false;
120✔
76
    this.address = new NetAddress();
120✔
77
    this.local = new NetAddress();
120✔
78
    this.name = null;
120✔
79
    this.connected = false;
120✔
80
    this.destroyed = false;
120✔
81
    this.ack = false;
120✔
82
    this.handshake = false;
120✔
83
    this.time = 0;
120✔
84
    this.lastSend = 0;
120✔
85
    this.lastRecv = 0;
120✔
86
    this.drainSize = 0;
120✔
87
    this.drainQueue = [];
120✔
88
    this.banScore = 0;
120✔
89
    this.invQueue = [];
120✔
90
    this.onPacket = null;
120✔
91

92
    this.next = null;
120✔
93
    this.prev = null;
120✔
94

95
    this.version = -1;
120✔
96
    this.services = 0;
120✔
97
    this.height = -1;
120✔
98
    this.agent = null;
120✔
99
    this.noRelay = false;
120✔
100
    this.preferHeaders = false;
120✔
101
    this.hashContinue = consensus.ZERO_HASH;
120✔
102
    this.spvFilter = null;
120✔
103
    this.feeRate = -1;
120✔
104
    this.compactMode = -1;
120✔
105
    this.merkleBlock = null;
120✔
106
    this.merkleTime = -1;
120✔
107
    this.merkleMatches = 0;
120✔
108
    this.merkleMap = null;
120✔
109
    this.syncing = false;
120✔
110
    this.sentAddr = false;
120✔
111
    this.gettingAddr = false;
120✔
112
    this.sentGetAddr = false;
120✔
113
    this.challenge = null;
120✔
114
    this.lastPong = -1;
120✔
115
    this.lastPing = -1;
120✔
116
    this.minPing = -1;
120✔
117
    this.blockTime = -1;
120✔
118

119
    this.bestHash = consensus.ZERO_HASH;
120✔
120
    this.bestHeight = -1;
120✔
121

122
    this.lastTip = consensus.ZERO_HASH;
120✔
123
    this.lastStop = consensus.ZERO_HASH;
120✔
124

125
    this.connectTimeout = null;
120✔
126
    this.pingTimer = null;
120✔
127
    this.invTimer = null;
120✔
128
    this.stallTimer = null;
120✔
129

130
    this.addrFilter = new RollingFilter(5000, 0.001);
120✔
131
    this.invFilter = new RollingFilter(50000, 0.000001);
120✔
132

133
    this.blockMap = new BufferMap();
120✔
134
    this.txMap = new BufferMap();
120✔
135
    this.claimMap = new BufferMap();
120✔
136
    this.airdropMap = new BufferMap();
120✔
137
    this.responseMap = new Map();
120✔
138
    this.compactBlocks = new BufferMap();
120✔
139
    this.nameMap = new BufferMap();
120✔
140
    this.totalProofs = 0;
120✔
141

142
    this.proofWindow = null;
120✔
143
    this.init();
120✔
144
  }
145

146
  /**
147
   * Create inbound peer from socket.
148
   * @param {PeerOptions} options
149
   * @param {NetSocket} socket
150
   * @param {Boolean} encrypted
151
   * @returns {Peer}
152
   */
153

154
  static fromInbound(options, socket, encrypted) {
155
    const peer = new this(options);
45✔
156
    peer.accept(socket, encrypted);
45✔
157
    return peer;
45✔
158
  }
159

160
  /**
161
   * Create outbound peer from net address.
162
   * @param {PeerOptions} options
163
   * @param {NetAddress} addr
164
   * @returns {Peer}
165
   */
166

167
  static fromOutbound(options, addr) {
168
    const peer = new this(options);
75✔
169
    peer.connect(addr);
75✔
170
    return peer;
75✔
171
  }
172

173
  /**
174
   * Begin peer initialization.
175
   * @private
176
   */
177

178
  init() {
179
    this.parser.on('packet', async (packet) => {
120✔
180
      try {
2,734✔
181
        await this.readPacket(packet);
2,734✔
182
      } catch (e) {
183
        this.error(e);
17✔
184
        this.destroy();
17✔
185
      }
186
    });
187

188
    this.parser.on('error', (err) => {
120✔
189
      if (this.destroyed)
×
190
        return;
×
191

192
      this.error(err);
×
193

194
      try {
×
195
        this.sendReject('malformed', 'error parsing message');
×
196
        this.increaseBan(10);
×
197
      } catch (e) {
198
        this.error(e);
×
199
      }
200
    });
201
  }
202

203
  /**
204
   * Getter to retrieve hostname.
205
   * @returns {String}
206
   */
207

208
  hostname() {
209
    return this.address.hostname;
5,406✔
210
  }
211

212
  /**
213
   * Frame a payload with a header.
214
   * @param {String} cmd - Packet type.
215
   * @param {Buffer} payload
216
   * @returns {Buffer} Payload with header prepended.
217
   */
218

219
  framePacket(cmd, payload) {
220
    return this.framer.packet(cmd, payload);
2,773✔
221
  }
222

223
  /**
224
   * Feed data to the parser.
225
   * @param {Buffer} data
226
   */
227

228
  feedParser(data) {
229
    return this.parser.feed(data);
2,340✔
230
  }
231

232
  /**
233
   * Bind to socket.
234
   * @param {net.Socket} socket
235
   */
236

237
  _bind(socket, encrypted) {
238
    assert(!this.socket);
120✔
239

240
    this.socket = socket;
120✔
241
    this.encrypted = encrypted;
120✔
242
    this.stream = encrypted ? this.brontide : this.socket;
120!
243

244
    this.brontide.on('error', (err) => {
120✔
245
      this.error(err);
×
246
      this.destroy();
×
247
    });
248

249
    this.socket.on('error', (err) => {
120✔
250
      if (!this.connected)
4!
251
        return;
×
252

253
      this.error(err);
4✔
254
      this.destroy();
4✔
255
    });
256

257
    this.socket.once('close', () => {
120✔
258
      this.error('Socket hangup.');
120✔
259
      this.destroy();
120✔
260
    });
261

262
    this.socket.on('drain', () => {
120✔
263
      this.handleDrain();
10✔
264
    });
265

266
    this.stream.on('data', (chunk) => {
120✔
267
      try {
2,340✔
268
        this.lastRecv = Date.now();
2,340✔
269
        this.feedParser(chunk);
2,340✔
270
      } catch (e) {
271
        this.error(e);
×
272
        this.destroy();
×
273
      }
274
    });
275

276
    this.socket.setNoDelay(true);
120✔
277
  }
278

279
  /**
280
   * Accept an inbound socket.
281
   * @param {net.Socket} socket
282
   * @returns {net.Socket}
283
   */
284

285
  accept(socket, encrypted) {
286
    assert(!this.socket);
45✔
287

288
    this.address = NetAddress.fromSocket(socket, this.network);
45✔
289
    this.address.services = 0;
45✔
290
    this.outbound = false;
45✔
291

292
    this._bind(socket, encrypted);
45✔
293

294
    if (encrypted) {
45!
295
      this.connected = false;
×
296
      this.brontide.accept(socket, this.identityKey);
×
297
    } else {
298
      this.time = Date.now();
45✔
299
      this.connected = true;
45✔
300
    }
301

302
    return socket;
45✔
303
  }
304

305
  /**
306
   * Create the socket and begin connecting. This method
307
   * will use `options.createSocket` if provided.
308
   * @param {NetAddress} addr
309
   * @returns {net.Socket}
310
   */
311

312
  connect(addr) {
313
    assert(!this.socket);
75✔
314

315
    const socket = this.options.createSocket(addr.port, addr.host);
75✔
316

317
    this.address = addr;
75✔
318
    this.outbound = true;
75✔
319
    this.connected = false;
75✔
320

321
    this._bind(socket, addr.hasKey());
75✔
322

323
    if (addr.hasKey())
75!
324
      this.brontide.connect(socket, this.identityKey, addr.key);
×
325

326
    return socket;
75✔
327
  }
328

329
  /**
330
   * Do a reverse dns lookup on peer's addr.
331
   * @returns {Promise}
332
   */
333

334
  async getName() {
335
    try {
×
336
      if (!this.name) {
×
337
        const {host, port} = this.address;
×
338
        const {hostname} = await dns.lookupService(host, port);
×
339
        this.name = hostname;
×
340
      }
341
    } catch (e) {
342
      ;
343
    }
344
    return this.name;
×
345
  }
346

347
  /**
348
   * Open and perform initial handshake (without rejection).
349
   * @method
350
   * @returns {Promise}
351
   */
352

353
  async tryOpen() {
354
    try {
120✔
355
      await this.open();
120✔
356
    } catch (e) {
357
      ;
358
    }
359
  }
360

361
  /**
362
   * Open and perform initial handshake.
363
   * @method
364
   * @returns {Promise}
365
   */
366

367
  async open() {
368
    try {
120✔
369
      await this._open();
120✔
370
    } catch (e) {
371
      this.error(e);
×
372
      this.destroy();
×
373
      throw e;
×
374
    }
375
  }
376

377
  /**
378
   * Open and perform initial handshake.
379
   * @method
380
   * @returns {Promise}
381
   */
382

383
  async _open() {
384
    this.opened = true;
120✔
385

386
    // Connect to peer.
387
    await this.initConnect();
120✔
388
    await this.initStall();
90✔
389
    await this.initVersion();
90✔
390
    await this.finalize();
90✔
391

392
    assert(!this.destroyed);
90✔
393

394
    // Finally we can let the pool know
395
    // that this peer is ready to go.
396
    this.emit('open');
90✔
397
  }
398

399
  /**
400
   * Wait for connection.
401
   * @private
402
   * @returns {Promise}
403
   */
404

405
  async initConnect() {
406
    if (this.connected) {
120✔
407
      assert(!this.outbound);
45✔
408
      return Promise.resolve();
45✔
409
    }
410

411
    assert(this.stream);
75✔
412
    assert(this.socket);
75✔
413

414
    return new Promise((resolve, reject) => {
75✔
415
      const cleanup = () => {
75✔
416
        if (this.connectTimeout != null) {
45!
417
          clearTimeout(this.connectTimeout);
45✔
418
          this.connectTimeout = null;
45✔
419
        }
420

421
        if (this.socket) {
45!
422
          // eslint-disable-next-line no-use-before-define
423
          this.socket.removeListener('error', onError);
45✔
424
        }
425

426
        // eslint-disable-next-line no-use-before-define
427
        this.brontide.removeListener('error', onError);
45✔
428
      };
429

430
      const onError = (err) => {
75✔
431
        cleanup();
×
432
        reject(err);
×
433
      };
434

435
      this.stream.once('connect', () => {
75✔
436
        this.time = Date.now();
45✔
437
        this.connected = true;
45✔
438
        this.emit('connect');
45✔
439

440
        cleanup();
45✔
441
        resolve();
45✔
442
      });
443

444
      this.socket.once('error', onError);
75✔
445
      this.brontide.once('error', onError);
75✔
446

447
      this.connectTimeout = setTimeout(() => {
75✔
448
        this.connectTimeout = null;
×
449
        cleanup();
×
450
        reject(new Error('Connection timed out.'));
×
451
      }, Peer.CONNECT_TIMEOUT);
452
    });
453
  }
454

455
  /**
456
   * Setup stall timer.
457
   * @private
458
   * @returns {Promise}
459
   */
460

461
  initStall() {
462
    assert(!this.stallTimer);
90✔
463
    assert(!this.destroyed);
90✔
464
    this.stallTimer = setInterval(() => {
90✔
465
      this.maybeTimeout();
2✔
466
    }, Peer.STALL_INTERVAL);
467
    return Promise.resolve();
90✔
468
  }
469

470
  /**
471
   * Handle post handshake.
472
   * @method
473
   * @private
474
   * @returns {Promise}
475
   */
476

477
  async initVersion() {
478
    assert(!this.destroyed);
90✔
479

480
    if (this.outbound) {
90✔
481
      if (this.version !== -1)
45!
482
        throw new Error('Peer prematurely introduced themselves (outbound).');
×
483

484
      if (this.ack)
45!
485
        throw new Error('Peer prematurely acknowledged us (outbound).');
×
486

487
      // Say hello.
488
      this.sendVersion();
45✔
489

490
      await this.wait(packetTypes.VERACK, Peer.HANDSHAKE_TIMEOUT);
45✔
491

492
      assert(this.ack);
45✔
493

494
      if (this.version === -1)
45!
495
        await this.wait(packetTypes.VERSION, Peer.HANDSHAKE_TIMEOUT);
45✔
496

497
      assert(this.version !== -1);
45✔
498
    } else {
499
      // We're shy. Wait for an introduction.
500
      if (this.version === -1)
45!
501
        await this.wait(packetTypes.VERSION, Peer.HANDSHAKE_TIMEOUT);
45✔
502

503
      assert(this.version !== -1);
45✔
504

505
      if (this.ack)
45!
506
        throw new Error('Peer prematurely acknowledged us (inbound).');
×
507

508
      this.sendVersion();
45✔
509

510
      await this.wait(packetTypes.VERACK, Peer.HANDSHAKE_TIMEOUT);
45✔
511
    }
512

513
    if (this.destroyed)
90!
514
      throw new Error('Peer was destroyed during handshake.');
×
515

516
    this.handshake = true;
90✔
517

518
    this.logger.debug('Version handshake complete (%s).', this.hostname());
90✔
519
  }
520

521
  /**
522
   * Finalize peer after handshake.
523
   * @method
524
   * @private
525
   * @returns {Promise}
526
   */
527

528
  async finalize() {
529
    assert(!this.destroyed);
90✔
530

531
    // Setup the ping interval.
532
    this.pingTimer = setInterval(() => {
90✔
533
      this.sendPing();
×
534
    }, Peer.PING_INTERVAL);
535

536
    // Setup the inv flusher.
537
    this.invTimer = setInterval(() => {
90✔
538
      this.flushInv();
2✔
539
    }, Peer.INV_INTERVAL);
540

541
    this.proofWindow = new SlidingWindow({
90✔
542
      limit: this.options.maxProofRPS
543
    });
544
    this.proofWindow.start();
90✔
545
  }
546

547
  /**
548
   * Broadcast blocks to peer.
549
   * @param {Block[]} blocks
550
   */
551

552
  announceBlock(blocks) {
553
    if (!this.handshake)
967!
554
      return;
×
555

556
    if (this.destroyed)
967!
557
      return;
×
558

559
    if (!Array.isArray(blocks))
967!
560
      blocks = [blocks];
967✔
561

562
    const inv = [];
967✔
563

564
    for (const block of blocks) {
967✔
565
      assert(block instanceof Block);
967✔
566

567
      // Don't send if they already have it.
568
      if (this.invFilter.test(block.hash()))
967✔
569
        continue;
362✔
570

571
      // Send them the block immediately if
572
      // they're using compact block mode 1.
573
      if (this.compactMode === 1) {
605!
574
        this.invFilter.add(block.hash());
×
575
        this.sendCompactBlock(block);
×
576
        continue;
×
577
      }
578

579
      // Convert item to block headers
580
      // for peers that request it.
581
      if (this.preferHeaders) {
605!
582
        inv.push(block.toHeaders());
×
583
        continue;
×
584
      }
585

586
      inv.push(block.toInv());
605✔
587
    }
588

589
    if (this.preferHeaders) {
967!
590
      this.sendHeaders(inv);
×
591
      return;
×
592
    }
593

594
    this.queueInv(inv);
967✔
595
  }
596

597
  /**
598
   * Broadcast transactions to peer.
599
   * @param {TX[]} txs
600
   */
601

602
  announceTX(txs) {
603
    if (!this.handshake)
60!
604
      return;
×
605

606
    if (this.destroyed)
60!
607
      return;
×
608

609
    // Do not send txs to spv clients
610
    // that have relay unset.
611
    if (this.noRelay)
60!
612
      return;
×
613

614
    if (!Array.isArray(txs))
60!
615
      txs = [txs];
60✔
616

617
    const inv = [];
60✔
618

619
    for (const tx of txs) {
60✔
620
      assert(tx instanceof TX);
60✔
621

622
      // Don't send if they already have it.
623
      if (this.invFilter.test(tx.hash()))
60✔
624
        continue;
3✔
625

626
      // Check the peer's bloom
627
      // filter if they're using spv.
628
      if (this.spvFilter) {
57✔
629
        if (!tx.testAndMaybeUpdate(this.spvFilter))
10✔
630
          continue;
8✔
631
      }
632

633
      // Check the fee filter.
634
      if (this.feeRate !== -1) {
49!
635
        const rate = this.options.getRate(tx.hash());
×
636
        if (rate !== -1 && rate < this.feeRate)
×
637
          continue;
×
638
      }
639

640
      inv.push(tx.toInv());
49✔
641
    }
642

643
    this.queueInv(inv);
60✔
644
  }
645

646
  /**
647
   * Broadcast transactions to peer.
648
   * @param {Claim[]} claims
649
   */
650

651
  announceClaim(claims) {
652
    if (!this.handshake)
×
653
      return;
×
654

655
    if (this.destroyed)
×
656
      return;
×
657

658
    // Do not send claims to spv clients
659
    // that have relay unset.
660
    if (this.noRelay)
×
661
      return;
×
662

663
    if (!Array.isArray(claims))
×
664
      claims = [claims];
×
665

666
    const inv = [];
×
667

668
    for (const claim of claims) {
×
669
      assert(claim instanceof Claim);
×
670

671
      // Don't send if they already have it.
672
      if (this.invFilter.test(claim.hash()))
×
673
        continue;
×
674

675
      inv.push(claim.toInv());
×
676
    }
677

678
    this.queueInv(inv);
×
679
  }
680

681
  /**
682
   * Broadcast transactions to peer.
683
   * @param {AirdropProof[]} proofs
684
   */
685

686
  announceAirdrop(proofs) {
687
    if (!this.handshake)
×
688
      return;
×
689

690
    if (this.destroyed)
×
691
      return;
×
692

693
    // Do not send proofs to spv clients
694
    // that have relay unset.
695
    if (this.noRelay)
×
696
      return;
×
697

698
    if (!Array.isArray(proofs))
×
699
      proofs = [proofs];
×
700

701
    const inv = [];
×
702

703
    for (const proof of proofs) {
×
704
      assert(proof instanceof AirdropProof);
×
705

706
      // Don't send if they already have it.
707
      if (this.invFilter.test(proof.hash()))
×
708
        continue;
×
709

710
      inv.push(proof.toInv(InvItem));
×
711
    }
712

713
    this.queueInv(inv);
×
714
  }
715

716
  /**
717
   * Send inv to a peer.
718
   * @param {InvItem[]} items
719
   */
720

721
  queueInv(items) {
722
    if (!this.handshake)
1,066!
723
      return;
×
724

725
    if (this.destroyed)
1,066!
726
      return;
×
727

728
    if (!Array.isArray(items))
1,066!
729
      items = [items];
×
730

731
    let hasBlock = false;
1,066✔
732

733
    for (const item of items) {
1,066✔
734
      if (item.type === invTypes.BLOCK)
658✔
735
        hasBlock = true;
605✔
736
      this.invQueue.push(item);
658✔
737
    }
738

739
    if (this.invQueue.length >= 500 || hasBlock)
1,066✔
740
      this.flushInv();
605✔
741
  }
742

743
  /**
744
   * Flush inv queue.
745
   * @private
746
   */
747

748
  flushInv() {
749
    if (this.destroyed)
607!
750
      return;
×
751

752
    const queue = this.invQueue;
607✔
753

754
    if (queue.length === 0)
607✔
755
      return;
1✔
756

757
    this.invQueue = [];
606✔
758

759
    this.logger.spam('Serving %d inv items to %s.',
606✔
760
      queue.length, this.hostname());
761

762
    const items = [];
606✔
763

764
    for (const item of queue) {
606✔
765
      if (!this.invFilter.added(item.hash))
653✔
766
        continue;
3✔
767

768
      items.push(item);
650✔
769
    }
770

771
    for (let i = 0; i < items.length; i += 1000) {
606✔
772
      const chunk = items.slice(i, i + 1000);
606✔
773
      this.send(new packets.InvPacket(chunk));
606✔
774
    }
775
  }
776

777
  /**
778
   * Force send an inv (no filter check).
779
   * @param {InvItem[]} items
780
   */
781

782
  sendInv(items) {
783
    if (!this.handshake)
57!
784
      return;
×
785

786
    if (this.destroyed)
57!
787
      return;
×
788

789
    if (!Array.isArray(items))
57!
790
      items = [items];
×
791

792
    for (const item of items)
57✔
793
      this.invFilter.add(item.hash);
365✔
794

795
    if (items.length === 0)
57✔
796
      return;
30✔
797

798
    this.logger.spam('Serving %d inv items to %s.',
27✔
799
      items.length, this.hostname());
800

801
    for (let i = 0; i < items.length; i += 1000) {
27✔
802
      const chunk = items.slice(i, i + 1000);
27✔
803
      this.send(new packets.InvPacket(chunk));
27✔
804
    }
805
  }
806

807
  /**
808
   * Send headers to a peer.
809
   * @param {Headers[]} items
810
   */
811

812
  sendHeaders(items) {
813
    if (!this.handshake)
×
814
      return;
×
815

816
    if (this.destroyed)
×
817
      return;
×
818

819
    if (!Array.isArray(items))
×
820
      items = [items];
×
821

822
    for (const item of items)
×
823
      this.invFilter.add(item.hash());
×
824

825
    if (items.length === 0)
×
826
      return;
×
827

828
    this.logger.spam('Serving %d headers to %s.',
×
829
      items.length, this.hostname());
830

831
    for (let i = 0; i < items.length; i += 2000) {
×
832
      const chunk = items.slice(i, i + 2000);
×
833
      this.send(new packets.HeadersPacket(chunk));
×
834
    }
835
  }
836

837
  /**
838
   * Send a compact block.
839
   * @private
840
   * @param {Block} block
841
   * @returns {Boolean}
842
   */
843

844
  sendCompactBlock(block) {
845
    const compact = BIP152.CompactBlock.fromBlock(block);
351✔
846
    this.send(new packets.CmpctBlockPacket(compact));
351✔
847
  }
848

849
  /**
850
   * Send a `version` packet.
851
   */
852

853
  sendVersion() {
854
    const packet = new packets.VersionPacket();
92✔
855
    packet.version = this.options.version;
92✔
856
    packet.services = this.options.services;
92✔
857
    packet.time = this.network.now();
92✔
858
    packet.remote = this.address;
92✔
859
    packet.nonce = this.options.createNonce(this.hostname());
92✔
860
    packet.agent = this.options.agent;
92✔
861
    packet.height = this.options.getHeight();
92✔
862
    packet.noRelay = this.options.noRelay;
92✔
863
    this.send(packet);
92✔
864
  }
865

866
  /**
867
   * Send a `getaddr` packet.
868
   */
869

870
  sendGetAddr() {
871
    if (this.sentGetAddr)
45!
872
      return;
×
873

874
    this.sentGetAddr = true;
45✔
875
    this.send(new packets.GetAddrPacket());
45✔
876
  }
877

878
  /**
879
   * Send a `ping` packet.
880
   */
881

882
  sendPing() {
883
    if (!this.handshake)
×
884
      return;
×
885

886
    if (this.challenge) {
×
887
      this.logger.debug(
×
888
        'Peer has not responded to ping (%s).',
889
        this.hostname());
890
      return;
×
891
    }
892

893
    this.lastPing = Date.now();
×
894
    this.challenge = common.nonce();
×
895

896
    this.send(new packets.PingPacket(this.challenge));
×
897
  }
898

899
  /**
900
   * Send `filterload` to update the local bloom filter.
901
   */
902

903
  sendFilterLoad(filter) {
904
    if (!this.handshake)
10!
905
      return;
×
906

907
    if (!this.options.spv)
10!
908
      return;
×
909

910
    if (!(this.services & services.BLOOM))
10!
911
      return;
×
912

913
    this.send(new packets.FilterLoadPacket(filter));
10✔
914
  }
915

916
  /**
917
   * Set a fee rate filter for the peer.
918
   * @param {Rate} rate
919
   */
920

921
  sendFeeRate(rate) {
922
    if (!this.handshake)
×
923
      return;
×
924

925
    this.send(new packets.FeeFilterPacket(rate));
×
926
  }
927

928
  /**
929
   * Disconnect from and destroy the peer.
930
   */
931

932
  destroy() {
933
    const connected = this.connected;
222✔
934

935
    if (this.destroyed)
222✔
936
      return;
102✔
937

938
    this.destroyed = true;
120✔
939
    this.connected = false;
120✔
940

941
    this.socket.destroy();
120✔
942
    this.socket = null;
120✔
943

944
    if (this.pingTimer != null) {
120✔
945
      clearInterval(this.pingTimer);
90✔
946
      this.pingTimer = null;
90✔
947
    }
948

949
    if (this.invTimer != null) {
120✔
950
      clearInterval(this.invTimer);
90✔
951
      this.invTimer = null;
90✔
952
    }
953

954
    if (this.proofWindow != null) {
120✔
955
      this.proofWindow.stop();
90✔
956
    }
957

958
    if (this.stallTimer != null) {
120✔
959
      clearInterval(this.stallTimer);
90✔
960
      this.stallTimer = null;
90✔
961
    }
962

963
    if (this.connectTimeout != null) {
120✔
964
      clearTimeout(this.connectTimeout);
30✔
965
      this.connectTimeout = null;
30✔
966
    }
967

968
    const jobs = this.drainQueue;
120✔
969

970
    this.drainSize = 0;
120✔
971
    this.drainQueue = [];
120✔
972

973
    for (const job of jobs)
120✔
974
      job.reject(new Error('Peer was destroyed.'));
×
975

976
    for (const [cmd, entry] of this.responseMap) {
120✔
977
      this.responseMap.delete(cmd);
2✔
978
      entry.reject(new Error('Peer was destroyed.'));
2✔
979
    }
980

981
    this.locker.destroy();
120✔
982

983
    this.emit('close', connected);
120✔
984
  }
985

986
  /**
987
   * Write data to the peer's socket.
988
   * @param {Buffer} data
989
   */
990

991
  write(data) {
992
    if (this.destroyed)
2,773✔
993
      throw new Error('Peer is destroyed (write).');
1✔
994

995
    this.lastSend = Date.now();
2,772✔
996

997
    if (this.stream.write(data) === false)
2,772✔
998
      this.needsDrain(data.length);
10✔
999
  }
1000

1001
  /**
1002
   * Send a packet.
1003
   * @param {Packet} packet
1004
   */
1005

1006
  send(packet) {
1007
    if (this.destroyed)
2,687!
1008
      throw new Error('Peer is destroyed (send).');
×
1009

1010
    this.sendRaw(packet.rawType, packet.encode());
2,687✔
1011
    this.addTimeout(packet);
2,687✔
1012
  }
1013

1014
  /**
1015
   * Send a packet.
1016
   * @param {Packet} packet
1017
   */
1018

1019
  sendRaw(type, body) {
1020
    const payload = this.framePacket(type, body);
2,773✔
1021
    this.write(payload);
2,773✔
1022
  }
1023

1024
  /**
1025
   * Wait for a drain event.
1026
   * @returns {Promise}
1027
   */
1028

1029
  drain() {
1030
    if (this.destroyed)
892!
1031
      return Promise.reject(new Error('Peer is destroyed.'));
×
1032

1033
    if (this.drainSize === 0)
892!
1034
      return Promise.resolve();
892✔
1035

1036
    return new Promise((resolve, reject) => {
×
1037
      this.drainQueue.push({ resolve, reject });
×
1038
    });
1039
  }
1040

1041
  /**
1042
   * Handle drain event.
1043
   * @private
1044
   */
1045

1046
  handleDrain() {
1047
    const jobs = this.drainQueue;
10✔
1048

1049
    this.drainSize = 0;
10✔
1050

1051
    if (jobs.length === 0)
10!
1052
      return;
10✔
1053

1054
    this.drainQueue = [];
×
1055

1056
    for (const job of jobs)
×
1057
      job.resolve();
×
1058
  }
1059

1060
  /**
1061
   * Add to drain counter.
1062
   * @private
1063
   * @param {Number} size
1064
   */
1065

1066
  needsDrain(size) {
1067
    this.drainSize += size;
10✔
1068

1069
    if (this.drainSize >= Peer.DRAIN_MAX) {
10!
1070
      this.logger.warning(
×
1071
        'Peer is not reading: %dmb buffered (%s).',
1072
        this.drainSize / (1 << 20),
1073
        this.hostname());
1074
      this.error('Peer stalled (drain).');
×
1075
      this.destroy();
×
1076
    }
1077
  }
1078

1079
  /**
1080
   * Potentially add response timeout.
1081
   * @private
1082
   * @param {Packet} packet
1083
   */
1084

1085
  addTimeout(packet) {
1086
    const timeout = Peer.RESPONSE_TIMEOUT;
2,687✔
1087

1088
    if (!this.outbound)
2,687✔
1089
      return;
1,701✔
1090

1091
    switch (packet.type) {
986!
1092
      case packetTypes.GETBLOCKS:
1093
        if (!this.options.isFull())
57!
1094
          this.request(packetTypes.INV, timeout);
×
1095
        break;
57✔
1096
      case packetTypes.GETHEADERS:
1097
        this.request(packetTypes.HEADERS, timeout * 2);
×
1098
        break;
×
1099
      case packetTypes.GETDATA:
1100
        this.request(packetTypes.DATA, timeout * 2);
613✔
1101
        break;
613✔
1102
      case packetTypes.GETBLOCKTXN:
1103
        this.request(packetTypes.BLOCKTXN, timeout);
38✔
1104
        break;
38✔
1105
      case packetTypes.GETPROOF:
1106
        this.request(packetTypes.PROOF, timeout);
54✔
1107
        break;
54✔
1108
    }
1109
  }
1110

1111
  /**
1112
   * Potentially finish response timeout.
1113
   * @private
1114
   * @param {Packet} packet
1115
   */
1116

1117
  fulfill(packet) {
1118
    switch (packet.type) {
2,719✔
1119
      case packetTypes.BLOCK:
1120
      case packetTypes.CMPCTBLOCK:
1121
      case packetTypes.MERKLEBLOCK:
1122
      case packetTypes.TX:
1123
      case packetTypes.CLAIM:
1124
      case packetTypes.AIRDROP:
1125
      case packetTypes.NOTFOUND: {
1126
        const entry = this.response(packetTypes.DATA, packet);
914✔
1127
        assert(!entry || entry.jobs.length === 0);
914✔
1128
        break;
914✔
1129
      }
1130
    }
1131

1132
    return this.response(packet.type, packet);
2,719✔
1133
  }
1134

1135
  /**
1136
   * Potentially timeout peer if it hasn't responded.
1137
   * @private
1138
   */
1139

1140
  maybeTimeout() {
1141
    const now = Date.now();
2✔
1142

1143
    for (const [key, entry] of this.responseMap) {
2✔
1144
      if (now > entry.timeout) {
×
1145
        const name = packets.typesByVal[key];
×
1146
        this.error('Peer is stalling (%s).', name.toLowerCase());
×
1147
        this.destroy();
×
1148
        return;
×
1149
      }
1150
    }
1151

1152
    if (this.merkleBlock) {
2!
1153
      assert(this.merkleTime !== -1);
×
1154
      if (now > this.merkleTime + Peer.BLOCK_TIMEOUT) {
×
1155
        this.error('Peer is stalling (merkleblock).');
×
1156
        this.destroy();
×
1157
        return;
×
1158
      }
1159
    }
1160

1161
    if (this.syncing && this.loader && !this.options.isFull()) {
2!
1162
      if (now > this.blockTime + Peer.BLOCK_TIMEOUT) {
×
1163
        this.error('Peer is stalling (block).');
×
1164
        this.destroy();
×
1165
        return;
×
1166
      }
1167
    }
1168

1169
    if (this.options.isFull() || !this.syncing) {
2!
1170
      for (const time of this.blockMap.values()) {
2✔
1171
        if (now > time + Peer.BLOCK_TIMEOUT) {
×
1172
          this.error('Peer is stalling (block).');
×
1173
          this.destroy();
×
1174
          return;
×
1175
        }
1176
      }
1177

1178
      for (const time of this.txMap.values()) {
2✔
1179
        if (now > time + Peer.TX_TIMEOUT) {
×
1180
          this.error('Peer is stalling (tx).');
×
1181
          this.destroy();
×
1182
          return;
×
1183
        }
1184
      }
1185

1186
      for (const time of this.claimMap.values()) {
2✔
1187
        if (now > time + Peer.TX_TIMEOUT) {
×
1188
          this.error('Peer is stalling (claim).');
×
1189
          this.destroy();
×
1190
          return;
×
1191
        }
1192
      }
1193

1194
      for (const time of this.airdropMap.values()) {
2✔
1195
        if (now > time + Peer.TX_TIMEOUT) {
×
1196
          this.error('Peer is stalling (airdrop).');
×
1197
          this.destroy();
×
1198
          return;
×
1199
        }
1200
      }
1201

1202
      for (const block of this.compactBlocks.values()) {
2✔
1203
        if (now > block.now + Peer.RESPONSE_TIMEOUT) {
×
1204
          this.error('Peer is stalling (blocktxn).');
×
1205
          this.destroy();
×
1206
          return;
×
1207
        }
1208
      }
1209
    }
1210

1211
    for (const time of this.nameMap.values()) {
2✔
1212
      if (now > time + Peer.NAME_TIMEOUT) {
×
1213
        this.error('Peer is stalling (name).');
×
1214
        this.destroy();
×
1215
        return;
×
1216
      }
1217
    }
1218

1219
    if (now > this.time + 60000) {
2!
1220
      assert(this.time !== 0);
×
1221

1222
      if (this.lastRecv === 0 || this.lastSend === 0) {
×
1223
        this.error('Peer is stalling (no message).');
×
1224
        this.destroy();
×
1225
        return;
×
1226
      }
1227

1228
      if (now > this.lastSend + Peer.TIMEOUT_INTERVAL) {
×
1229
        this.error('Peer is stalling (send).');
×
1230
        this.destroy();
×
1231
        return;
×
1232
      }
1233

1234
      if (now > this.lastRecv + Peer.TIMEOUT_INTERVAL) {
×
1235
        this.error('Peer is stalling (recv).');
×
1236
        this.destroy();
×
1237
        return;
×
1238
      }
1239

1240
      if (this.challenge && now > this.lastPing + Peer.TIMEOUT_INTERVAL) {
×
1241
        this.error('Peer is stalling (ping).');
×
1242
        this.destroy();
×
1243
        return;
×
1244
      }
1245
    }
1246
  }
1247

1248
  /**
1249
   * Wait for a packet to be received from peer.
1250
   * @private
1251
   * @param {Number} type - Packet type.
1252
   * @param {Number} timeout
1253
   * @returns {RequestEntry}
1254
   */
1255

1256
  request(type, timeout) {
1257
    if (this.destroyed)
885!
1258
      return null;
×
1259

1260
    let entry = this.responseMap.get(type);
885✔
1261

1262
    if (!entry) {
885✔
1263
      entry = new RequestEntry();
713✔
1264

1265
      this.responseMap.set(type, entry);
713✔
1266

1267
      if (this.responseMap.size >= common.MAX_REQUEST) {
713!
1268
        this.destroy();
×
1269
        return null;
×
1270
      }
1271
    }
1272

1273
    entry.setTimeout(timeout);
885✔
1274

1275
    return entry;
885✔
1276
  }
1277

1278
  /**
1279
   * Fulfill awaiting requests created with {@link Peer#request}.
1280
   * @private
1281
   * @param {Number} type - Packet type.
1282
   * @param {Object} payload
1283
   */
1284

1285
  response(type, payload) {
1286
    const entry = this.responseMap.get(type);
3,633✔
1287

1288
    if (!entry)
3,633✔
1289
      return null;
2,922✔
1290

1291
    this.responseMap.delete(type);
711✔
1292

1293
    return entry;
711✔
1294
  }
1295

1296
  /**
1297
   * Wait for a packet to be received from peer.
1298
   * @private
1299
   * @param {Number} type - Packet type.
1300
   * @returns {Promise} - Returns Object(payload).
1301
   * Executed on timeout or once packet is received.
1302
   */
1303

1304
  wait(type, timeout) {
1305
    return new Promise((resolve, reject) => {
180✔
1306
      const entry = this.request(type);
180✔
1307

1308
      if (!entry) {
180!
1309
        reject(new Error('Peer is destroyed (request).'));
×
1310
        return;
×
1311
      }
1312

1313
      entry.setTimeout(timeout);
180✔
1314
      entry.addJob(resolve, reject);
180✔
1315
    });
1316
  }
1317

1318
  /**
1319
   * Emit an error and destroy the peer.
1320
   * @private
1321
   * @param {...String|Error} err
1322
   */
1323

1324
  error(err) {
1325
    if (this.destroyed)
141✔
1326
      return;
102✔
1327

1328
    if (typeof err === 'string') {
39✔
1329
      const msg = format.apply(null, arguments);
34✔
1330
      err = new Error(msg);
34✔
1331
    }
1332

1333
    if (typeof err.code === 'string' && err.code[0] === 'E') {
39✔
1334
      const msg = err.code;
5✔
1335
      err = new Error(msg);
5✔
1336
      err.code = msg;
5✔
1337
      err.message = `Socket Error: ${msg}`;
5✔
1338
    }
1339

1340
    err.message += ` (${this.hostname()})`;
39✔
1341

1342
    this.emit('error', err);
39✔
1343
  }
1344

1345
  /**
1346
   * Calculate peer block inv type (filtered,
1347
   * compact, witness, or non-witness).
1348
   * @returns {Number}
1349
   */
1350

1351
  blockType() {
1352
    if (this.options.spv)
584✔
1353
      return invTypes.FILTERED_BLOCK;
203✔
1354

1355
    if (this.options.compact && this.hasCompact())
381!
1356
      return invTypes.CMPCT_BLOCK;
381✔
1357

1358
    return invTypes.BLOCK;
×
1359
  }
1360

1361
  /**
1362
   * Calculate peer tx inv type (witness or non-witness).
1363
   * @returns {Number}
1364
   */
1365

1366
  txType() {
1367
    return invTypes.TX;
29✔
1368
  }
1369

1370
  /**
1371
   * Send `getdata` to peer.
1372
   * @param {InvItem[]} items
1373
   */
1374

1375
  getData(items) {
1376
    this.send(new packets.GetDataPacket(items));
613✔
1377
  }
1378

1379
  /**
1380
   * Send batched `getdata` to peer.
1381
   * @param {InvType} type
1382
   * @param {Hash[]} hashes
1383
   */
1384

1385
  getItems(type, hashes) {
1386
    const items = [];
613✔
1387

1388
    for (const hash of hashes)
613✔
1389
      items.push(new InvItem(type, hash));
938✔
1390

1391
    if (items.length === 0)
613!
1392
      return;
×
1393

1394
    this.getData(items);
613✔
1395
  }
1396

1397
  /**
1398
   * Send batched `getdata` to peer (blocks).
1399
   * @param {Hash[]} hashes
1400
   */
1401

1402
  getBlock(hashes) {
1403
    this.getItems(this.blockType(), hashes);
584✔
1404
  }
1405

1406
  /**
1407
   * Send batched `getdata` to peer (txs).
1408
   * @param {Hash[]} hashes
1409
   */
1410

1411
  getTX(hashes) {
1412
    this.getItems(this.txType(), hashes);
29✔
1413
  }
1414

1415
  /**
1416
   * Send batched `getdata` to peer (claims).
1417
   * @param {Hash[]} hashes
1418
   */
1419

1420
  getClaim(hashes) {
1421
    this.getItems(invTypes.CLAIM, hashes);
×
1422
  }
1423

1424
  /**
1425
   * Send batched `getdata` to peer (airdrops).
1426
   * @param {Hash[]} hashes
1427
   */
1428

1429
  getAirdrop(hashes) {
1430
    this.getItems(invTypes.AIRDROP, hashes);
×
1431
  }
1432

1433
  /**
1434
   * Send `getdata` to peer for a single block.
1435
   * @param {Hash} hash
1436
   */
1437

1438
  getFullBlock(hash) {
1439
    assert(!this.options.spv);
×
1440
    this.getItems(invTypes.BLOCK, [hash]);
×
1441
  }
1442

1443
  /**
1444
   * Handle a packet payload.
1445
   * @method
1446
   * @private
1447
   * @param {Packet} packet
1448
   */
1449

1450
  async readPacket(packet) {
1451
    if (this.destroyed)
2,734!
1452
      return;
×
1453

1454
    // The "pre-handshake" packets get
1455
    // to bypass the lock, since they
1456
    // are meant to change the way input
1457
    // is handled at a low level. They
1458
    // must be handled immediately.
1459
    switch (packet.type) {
2,734!
1460
      case packetTypes.PONG: {
1461
        try {
×
1462
          this.socket.pause();
×
1463
          await this.handlePacket(packet);
×
1464
        } finally {
1465
          if (!this.destroyed && this.socket) {
×
1466
            try {
×
1467
              this.socket.resume();
×
1468
            } catch (e) {
1469
              ;
1470
            }
1471
          }
1472
        }
1473
        break;
×
1474
      }
1475
      default: {
1476
        const unlock = await this.locker.lock();
2,734✔
1477
        try {
2,719✔
1478
          this.socket.pause();
2,719✔
1479
          await this.handlePacket(packet);
2,719✔
1480
        } finally {
1481
          if (!this.destroyed && this.socket) {
2,719✔
1482
            try {
2,715✔
1483
              this.socket.resume();
2,715✔
1484
            } catch (e) {
1485
              ;
1486
            }
1487
          }
1488
          unlock();
2,719✔
1489
        }
1490
        break;
2,717✔
1491
      }
1492
    }
1493
  }
1494

1495
  /**
1496
   * Handle a packet payload without a lock.
1497
   * @method
1498
   * @private
1499
   * @param {Packet} packet
1500
   */
1501

1502
  async handlePacket(packet) {
1503
    if (this.destroyed)
2,719!
1504
      throw new Error('Destroyed peer sent a packet.');
×
1505

1506
    const entry = this.fulfill(packet);
2,719✔
1507

1508
    switch (packet.type) {
2,719!
1509
      case packetTypes.VERSION:
1510
        await this.handleVersion(packet);
90✔
1511
        break;
90✔
1512
      case packetTypes.VERACK:
1513
        await this.handleVerack(packet);
90✔
1514
        break;
90✔
1515
      case packetTypes.PING:
1516
        await this.handlePing(packet);
×
1517
        break;
×
1518
      case packetTypes.PONG:
1519
        await this.handlePong(packet);
×
1520
        break;
×
1521
      case packetTypes.SENDHEADERS:
1522
        await this.handleSendHeaders(packet);
×
1523
        break;
×
1524
      case packetTypes.FILTERLOAD:
1525
        await this.handleFilterLoad(packet);
10✔
1526
        break;
10✔
1527
      case packetTypes.FILTERADD:
1528
        await this.handleFilterAdd(packet);
×
1529
        break;
×
1530
      case packetTypes.FILTERCLEAR:
1531
        await this.handleFilterClear(packet);
×
1532
        break;
×
1533
      case packetTypes.FEEFILTER:
1534
        await this.handleFeeFilter(packet);
×
1535
        break;
×
1536
      case packetTypes.SENDCMPCT:
1537
        await this.handleSendCmpct(packet);
77✔
1538
        break;
77✔
1539
    }
1540

1541
    if (this.onPacket)
2,719!
1542
      await this.onPacket(packet);
2,719✔
1543

1544
    this.emit('packet', packet);
2,717✔
1545

1546
    if (entry)
2,717✔
1547
      entry.resolve(packet);
270✔
1548
  }
1549

1550
  /**
1551
   * Handle `version` packet.
1552
   * @method
1553
   * @private
1554
   * @param {VersionPacket} packet
1555
   */
1556

1557
  async handleVersion(packet) {
1558
    if (this.version !== -1)
90!
1559
      throw new Error('Peer sent a duplicate version.');
×
1560

1561
    this.version = packet.version;
90✔
1562
    this.services = packet.services;
90✔
1563
    this.height = packet.height;
90✔
1564
    this.agent = packet.agent;
90✔
1565
    this.noRelay = packet.noRelay;
90✔
1566
    this.local = packet.remote;
90✔
1567

1568
    if (!this.network.selfConnect) {
90✔
1569
      if (this.options.hasNonce(packet.nonce))
8!
1570
        throw new Error('We connected to ourself. Oops.');
×
1571
    }
1572

1573
    if (this.version < common.MIN_VERSION)
90!
1574
      throw new Error('Peer does not support required protocol version.');
×
1575

1576
    if (this.outbound) {
90✔
1577
      if (!(this.services & services.NETWORK))
45!
1578
        throw new Error('Peer does not support network services.');
×
1579

1580
      if (this.options.spv) {
45✔
1581
        if (!(this.services & services.BLOOM))
9!
1582
          throw new Error('Peer does not support BIP37.');
×
1583
      }
1584
    }
1585

1586
    this.send(new packets.VerackPacket());
90✔
1587
  }
1588

1589
  /**
1590
   * Handle `verack` packet.
1591
   * @method
1592
   * @private
1593
   * @param {VerackPacket} packet
1594
   */
1595

1596
  async handleVerack(packet) {
1597
    if (this.ack) {
90!
1598
      this.logger.debug('Peer sent duplicate ack (%s).', this.hostname());
×
1599
      return;
×
1600
    }
1601

1602
    this.ack = true;
90✔
1603
    this.logger.debug('Received verack (%s).', this.hostname());
90✔
1604
  }
1605

1606
  /**
1607
   * Handle `ping` packet.
1608
   * @method
1609
   * @private
1610
   * @param {PingPacket} packet
1611
   */
1612

1613
  async handlePing(packet) {
1614
    if (!packet.nonce)
×
1615
      return;
×
1616

1617
    this.send(new packets.PongPacket(packet.nonce));
×
1618
  }
1619

1620
  /**
1621
   * Handle `pong` packet.
1622
   * @method
1623
   * @private
1624
   * @param {PongPacket} packet
1625
   */
1626

1627
  async handlePong(packet) {
1628
    const nonce = packet.nonce;
×
1629
    const now = Date.now();
×
1630

1631
    if (!this.challenge) {
×
1632
      this.logger.debug('Peer sent an unsolicited pong (%s).', this.hostname());
×
1633
      return;
×
1634
    }
1635

1636
    if (!nonce.equals(this.challenge)) {
×
1637
      if (nonce.equals(common.ZERO_NONCE)) {
×
1638
        this.logger.debug('Peer sent a zero nonce (%s).', this.hostname());
×
1639
        this.challenge = null;
×
1640
        return;
×
1641
      }
1642
      this.logger.debug('Peer sent the wrong nonce (%s).', this.hostname());
×
1643
      return;
×
1644
    }
1645

1646
    if (now >= this.lastPing) {
×
1647
      this.lastPong = now;
×
1648
      if (this.minPing === -1)
×
1649
        this.minPing = now - this.lastPing;
×
1650
      this.minPing = Math.min(this.minPing, now - this.lastPing);
×
1651
    } else {
1652
      this.logger.debug('Timing mismatch (what?) (%s).', this.hostname());
×
1653
    }
1654

1655
    this.challenge = null;
×
1656
  }
1657

1658
  /**
1659
   * Handle `sendheaders` packet.
1660
   * @method
1661
   * @private
1662
   * @param {SendHeadersPacket} packet
1663
   */
1664

1665
  async handleSendHeaders(packet) {
1666
    if (this.preferHeaders) {
×
1667
      this.logger.debug(
×
1668
        'Peer sent a duplicate sendheaders (%s).',
1669
        this.hostname());
1670
      return;
×
1671
    }
1672

1673
    this.preferHeaders = true;
×
1674
  }
1675

1676
  /**
1677
   * Handle `filterload` packet.
1678
   * @method
1679
   * @private
1680
   * @param {FilterLoadPacket} packet
1681
   */
1682

1683
  async handleFilterLoad(packet) {
1684
    if (!packet.isWithinConstraints()) {
10!
1685
      this.increaseBan(100);
×
1686
      return;
×
1687
    }
1688

1689
    this.spvFilter = packet.filter;
10✔
1690
    this.noRelay = false;
10✔
1691
  }
1692

1693
  /**
1694
   * Handle `filteradd` packet.
1695
   * @method
1696
   * @private
1697
   * @param {FilterAddPacket} packet
1698
   */
1699

1700
  async handleFilterAdd(packet) {
1701
    const data = packet.data;
×
1702

1703
    if (data.length > consensus.MAX_SCRIPT_PUSH) {
×
1704
      this.increaseBan(100);
×
1705
      return;
×
1706
    }
1707

1708
    if (this.spvFilter)
×
1709
      this.spvFilter.add(data);
×
1710

1711
    this.noRelay = false;
×
1712
  }
1713

1714
  /**
1715
   * Handle `filterclear` packet.
1716
   * @method
1717
   * @private
1718
   * @param {FilterClearPacket} packet
1719
   */
1720

1721
  async handleFilterClear(packet) {
1722
    if (this.spvFilter)
×
1723
      this.spvFilter.reset();
×
1724

1725
    this.noRelay = false;
×
1726
  }
1727

1728
  /**
1729
   * Handle `feefilter` packet.
1730
   * @method
1731
   * @private
1732
   * @param {FeeFilterPacket} packet
1733
   */
1734

1735
  async handleFeeFilter(packet) {
1736
    const rate = packet.rate;
×
1737

1738
    if (rate < 0 || rate > consensus.MAX_MONEY) {
×
1739
      this.increaseBan(100);
×
1740
      return;
×
1741
    }
1742

1743
    this.feeRate = rate;
×
1744
  }
1745

1746
  /**
1747
   * Handle `sendcmpct` packet.
1748
   * @method
1749
   * @private
1750
   * @param {SendCmpctPacket}
1751
   */
1752

1753
  async handleSendCmpct(packet) {
1754
    if (this.compactMode !== -1) {
77!
1755
      this.logger.debug(
×
1756
        'Peer sent a duplicate sendcmpct (%s).',
1757
        this.hostname());
1758
      return;
×
1759
    }
1760

1761
    if (packet.version > 1) {
77!
1762
      // Ignore
1763
      this.logger.info(
×
1764
        'Peer request compact blocks version %d (%s).',
1765
        packet.version, this.hostname());
1766
      return;
×
1767
    }
1768

1769
    if (packet.mode > 1) {
77!
1770
      this.logger.info(
×
1771
        'Peer request compact blocks mode %d (%s).',
1772
        packet.mode, this.hostname());
1773
      return;
×
1774
    }
1775

1776
    this.logger.info(
77✔
1777
      'Peer initialized compact blocks (mode=%d, version=%d) (%s).',
1778
      packet.mode, packet.version, this.hostname());
1779

1780
    this.compactMode = packet.mode;
77✔
1781
  }
1782

1783
  /**
1784
   * Send `getheaders` to peer. Note that unlike
1785
   * `getblocks`, `getheaders` can have a null locator.
1786
   * @param {Hash[]} locator - Chain locator.
1787
   * @param {Hash} stop - Hash to stop at.
1788
   */
1789

1790
  sendGetHeaders(locator, stop) {
1791
    const packet = new packets.GetHeadersPacket(locator, stop);
×
1792

1793
    let hash = consensus.ZERO_HASH;
×
1794

1795
    if (packet.locator.length > 0)
×
1796
      hash = packet.locator[0];
×
1797

1798
    this.logger.debug(
×
1799
      'Requesting headers packet from peer with getheaders (%s).',
1800
      this.hostname());
1801

1802
    this.logger.debug(
×
1803
      'Sending getheaders (hash=%x, stop=%x).',
1804
      hash, stop);
1805

1806
    this.send(packet);
×
1807
  }
1808

1809
  /**
1810
   * Send `getblocks` to peer.
1811
   * @param {Hash[]} locator - Chain locator.
1812
   * @param {Hash} stop - Hash to stop at.
1813
   */
1814

1815
  sendGetBlocks(locator, stop) {
1816
    const packet = new packets.GetBlocksPacket(locator, stop);
67✔
1817

1818
    let hash = consensus.ZERO_HASH;
67✔
1819

1820
    if (packet.locator.length > 0)
67!
1821
      hash = packet.locator[0];
67✔
1822

1823
    if (hash.equals(this.lastTip) && stop.equals(this.lastStop))
67✔
1824
      return;
10✔
1825

1826
    this.lastTip = hash;
57✔
1827
    this.lastStop = stop;
57✔
1828

1829
    this.logger.debug(
57✔
1830
      'Requesting inv packet from peer with getblocks (%s).',
1831
      this.hostname());
1832

1833
    this.logger.debug(
57✔
1834
      'Sending getblocks (hash=%x, stop=%x).',
1835
      hash, stop);
1836

1837
    this.send(packet);
57✔
1838
  }
1839

1840
  /**
1841
   * Send `mempool` to peer.
1842
   */
1843

1844
  sendMempool() {
1845
    if (!this.handshake)
45!
1846
      return;
×
1847

1848
    if (!(this.services & services.BLOOM)) {
45✔
1849
      this.logger.debug(
6✔
1850
        'Cannot request mempool for non-bloom peer (%s).',
1851
        this.hostname());
1852
      return;
6✔
1853
    }
1854

1855
    this.logger.debug(
39✔
1856
      'Requesting inv packet from peer with mempool (%s).',
1857
      this.hostname());
1858

1859
    this.send(new packets.MempoolPacket());
39✔
1860
  }
1861

1862
  /**
1863
   * Send `reject` to peer.
1864
   * @param {Number} code
1865
   * @param {String} reason
1866
   * @param {Number} msg
1867
   * @param {Hash} hash
1868
   */
1869

1870
  sendReject(code, reason, msg, hash) {
1871
    const reject = packets.RejectPacket.fromReason(code, reason, msg, hash);
2✔
1872

1873
    if (msg != null) {
2!
1874
      this.logger.debug('Rejecting %s %x (%s): code=%s reason=%s.',
2✔
1875
        packets.typesByVal[msg] || 'UNKNOWN',
2!
1876
        hash, this.hostname(), code, reason);
1877
    } else {
1878
      this.logger.debug('Rejecting packet from %s: code=%s reason=%s.',
×
1879
        this.hostname(), code, reason);
1880
    }
1881

1882
    this.logger.debug(
2✔
1883
      'Sending reject packet to peer (%s).',
1884
      this.hostname());
1885

1886
    this.send(reject);
2✔
1887
  }
1888

1889
  /**
1890
   * Send a `sendcmpct` packet.
1891
   * @param {Number} mode
1892
   */
1893

1894
  sendCompact(mode) {
1895
    this.logger.info(
81✔
1896
      'Initializing normal compact blocks (%s).',
1897
      this.hostname());
1898

1899
    this.send(new packets.SendCmpctPacket(mode, 1));
81✔
1900
  }
1901

1902
  /**
1903
   * Send a `getproof` packet.
1904
   * @param {Buffer} root
1905
   * @param {Buffer} key
1906
   */
1907

1908
  sendGetProof(root, key) {
1909
    this.logger.info(
54✔
1910
      'Sending proof request for %x (%s).',
1911
      key,
1912
      this.hostname());
1913

1914
    this.send(new packets.GetProofPacket(root, key));
54✔
1915
  }
1916

1917
  /**
1918
   * Send a `proof` packet.
1919
   * @param {Number} version
1920
   * @param {Buffer} root
1921
   * @param {Buffer} key
1922
   * @param {Proof} proof
1923
   */
1924

1925
  sendProof(root, key, proof) {
1926
    this.logger.info(
53✔
1927
      'Sending proof (%s).',
1928
      this.hostname());
1929

1930
    this.proofWindow.increase(1);
53✔
1931

1932
    if (!this.proofWindow.allow()) {
53✔
1933
      this.logger.debug('proof: rate limit exceeded (%s).', this.hostname);
1✔
1934
      this.ban();
1✔
1935
      return;
1✔
1936
    }
1937

1938
    this.send(new packets.ProofPacket(root, key, proof));
52✔
1939
  }
1940

1941
  /**
1942
   * Increase banscore on peer.
1943
   * @param {Number} score
1944
   * @returns {Boolean}
1945
   */
1946

1947
  increaseBan(score) {
1948
    this.banScore += score;
2✔
1949

1950
    if (this.banScore >= this.options.banScore) {
2!
1951
      this.logger.debug('Ban threshold exceeded (%s).', this.hostname());
×
1952
      this.ban();
×
1953
      return true;
×
1954
    }
1955

1956
    return false;
2✔
1957
  }
1958

1959
  /**
1960
   * Ban peer.
1961
   */
1962

1963
  ban() {
1964
    this.emit('ban');
1✔
1965
  }
1966

1967
  /**
1968
   * Send a `reject` packet to peer.
1969
   * @param {Number} msg
1970
   * @param {VerifyError} err
1971
   * @returns {Boolean}
1972
   */
1973

1974
  reject(msg, err) {
1975
    this.sendReject(err.code, err.reason, msg, err.hash);
2✔
1976
    return this.increaseBan(err.score);
2✔
1977
  }
1978

1979
  /**
1980
   * Returns human readable list of services
1981
   * that are available.
1982
   * @returns {String[]}
1983
   */
1984

1985
  getServiceNames() {
1986
    const enabled = [];
×
1987

1988
    for (const [service, bit] of Object.entries(services)) {
×
1989
      if (this.hasServices(bit))
×
1990
        enabled.push(service);
×
1991
    }
1992

1993
    return enabled;
×
1994
  }
1995

1996
  /**
1997
   * Test whether required services are available.
1998
   * @param {Number} services
1999
   * @returns {Boolean}
2000
   */
2001

2002
  hasServices(services) {
2003
    return (this.services & services) === services;
×
2004
  }
2005

2006
  /**
2007
   * Test whether the peer sent us a
2008
   * compatible compact block handshake.
2009
   * @returns {Boolean}
2010
   */
2011

2012
  hasCompact() {
2013
    if (this.compactMode === -1)
720!
2014
      return false;
×
2015

2016
    return true;
720✔
2017
  }
2018

2019
  /**
2020
   * Inspect the peer.
2021
   * @returns {String}
2022
   */
2023

2024
  inspect() {
2025
    return '<Peer:'
×
2026
      + ` handshake=${this.handshake}`
2027
      + ` host=${this.hostname()}`
2028
      + ` outbound=${this.outbound}`
2029
      + ` ping=${this.minPing}`
2030
      + '>';
2031
  }
2032
}
2033

2034
/**
2035
 * Max output bytes buffered before
2036
 * invoking stall behavior for peer.
2037
 * @const {Number}
2038
 * @default
2039
 */
2040

2041
Peer.DRAIN_MAX = 10 << 20;
1✔
2042

2043
/**
2044
 * Interval to check for drainage
2045
 * and required responses from peer.
2046
 * @const {Number}
2047
 * @default
2048
 */
2049

2050
Peer.STALL_INTERVAL = 5000;
1✔
2051

2052
/**
2053
 * Interval for pinging peers.
2054
 * @const {Number}
2055
 * @default
2056
 */
2057

2058
Peer.PING_INTERVAL = 30000;
1✔
2059

2060
/**
2061
 * Interval to flush invs.
2062
 * Higher means more invs (usually
2063
 * txs) will be accumulated before
2064
 * flushing.
2065
 * @const {Number}
2066
 * @default
2067
 */
2068

2069
Peer.INV_INTERVAL = 5000;
1✔
2070

2071
/**
2072
 * Required time for peers to
2073
 * respond to messages (i.e.
2074
 * getblocks/getdata).
2075
 * @const {Number}
2076
 * @default
2077
 */
2078

2079
Peer.RESPONSE_TIMEOUT = 30000;
1✔
2080

2081
/**
2082
 * Required time for loader to
2083
 * respond with block/merkleblock.
2084
 * @const {Number}
2085
 * @default
2086
 */
2087

2088
Peer.BLOCK_TIMEOUT = 120000;
1✔
2089

2090
/**
2091
 * Required time for loader to
2092
 * respond with a tx.
2093
 * @const {Number}
2094
 * @default
2095
 */
2096

2097
Peer.TX_TIMEOUT = 120000;
1✔
2098

2099
/**
2100
 * Required time for peer to
2101
 * respond with a name.
2102
 * @const {Number}
2103
 * @default
2104
 */
2105

2106
Peer.NAME_TIMEOUT = 5000;
1✔
2107

2108
/**
2109
 * Generic timeout interval.
2110
 * @const {Number}
2111
 * @default
2112
 */
2113

2114
Peer.TIMEOUT_INTERVAL = 20 * 60000;
1✔
2115

2116
/**
2117
 * Connection timeout.
2118
 * @const {Number}
2119
 * @default
2120
 */
2121

2122
Peer.CONNECT_TIMEOUT = 5 * 1000;
1✔
2123

2124
/**
2125
 * Handshake timeout.
2126
 * @const {Number}
2127
 * @default
2128
 */
2129

2130
Peer.HANDSHAKE_TIMEOUT = 5 * 1000;
1✔
2131

2132
/**
2133
 * Peer Options
2134
 * @alias module:net.PeerOptions
2135
 */
2136

2137
class PeerOptions {
2138
  /**
2139
   * Create peer options.
2140
   * @constructor
2141
   */
2142

2143
  constructor(options) {
2144
    this.network = Network.primary;
120✔
2145
    this.logger = Logger.global;
120✔
2146

2147
    this.createSocket = tcp.createSocket;
120✔
2148
    this.version = common.PROTOCOL_VERSION;
120✔
2149
    this.services = common.LOCAL_SERVICES;
120✔
2150
    this.agent = common.USER_AGENT;
120✔
2151
    this.identityKey = common.ZERO_KEY;
120✔
2152
    this.noRelay = false;
120✔
2153
    this.spv = false;
120✔
2154
    this.compact = false;
120✔
2155
    this.headers = false;
120✔
2156
    this.banScore = common.BAN_SCORE;
120✔
2157
    this.maxProofRPS = 100;
120✔
2158

2159
    this.getHeight = PeerOptions.getHeight;
120✔
2160
    this.isFull = PeerOptions.isFull;
120✔
2161
    this.createNonce = PeerOptions.createNonce;
120✔
2162
    this.hasNonce = PeerOptions.hasNonce;
120✔
2163
    this.getRate = PeerOptions.getRate;
120✔
2164

2165
    if (options)
120!
2166
      this.fromOptions(options);
120✔
2167
  }
2168

2169
  /**
2170
   * Inject properties from object.
2171
   * @private
2172
   * @param {Object} options
2173
   * @returns {PeerOptions}
2174
   */
2175

2176
  fromOptions(options) {
2177
    assert(options, 'Options are required.');
120✔
2178

2179
    if (options.network != null)
120!
2180
      this.network = Network.get(options.network);
120✔
2181

2182
    if (options.logger != null) {
120!
2183
      assert(typeof options.logger === 'object');
120✔
2184
      this.logger = options.logger;
120✔
2185
    }
2186

2187
    if (options.createSocket != null) {
120!
2188
      assert(typeof options.createSocket === 'function');
120✔
2189
      this.createSocket = options.createSocket;
120✔
2190
    }
2191

2192
    if (options.version != null) {
120!
2193
      assert(typeof options.version === 'number');
120✔
2194
      this.version = options.version;
120✔
2195
    }
2196

2197
    if (options.services != null) {
120!
2198
      assert(typeof options.services === 'number');
120✔
2199
      this.services = options.services;
120✔
2200
    }
2201

2202
    if (options.agent != null) {
120!
2203
      assert(typeof options.agent === 'string');
120✔
2204
      this.agent = options.agent;
120✔
2205
    }
2206

2207
    if (options.identityKey != null) {
120!
2208
      assert(Buffer.isBuffer(options.identityKey));
120✔
2209
      assert(options.identityKey.length === 32);
120✔
2210
      this.identityKey = options.identityKey;
120✔
2211
    }
2212

2213
    if (options.noRelay != null) {
120!
2214
      assert(typeof options.noRelay === 'boolean');
120✔
2215
      this.noRelay = options.noRelay;
120✔
2216
    }
2217

2218
    if (options.spv != null) {
120!
2219
      assert(typeof options.spv === 'boolean');
120✔
2220
      this.spv = options.spv;
120✔
2221
    }
2222

2223
    if (options.compact != null) {
120!
2224
      assert(typeof options.compact === 'boolean');
120✔
2225
      this.compact = options.compact;
120✔
2226
    }
2227

2228
    if (options.headers != null) {
120!
2229
      assert(typeof options.headers === 'boolean');
×
2230
      this.headers = options.headers;
×
2231
    }
2232

2233
    if (options.banScore != null) {
120!
2234
      assert(typeof options.banScore === 'number');
120✔
2235
      this.banScore = options.banScore;
120✔
2236
    }
2237

2238
    if (options.maxProofRPS != null) {
120!
2239
      assert(typeof options.maxProofRPS === 'number');
120✔
2240
      this.maxProofRPS = options.maxProofRPS;
120✔
2241
    }
2242

2243
    if (options.getHeight != null) {
120!
2244
      assert(typeof options.getHeight === 'function');
120✔
2245
      this.getHeight = options.getHeight;
120✔
2246
    }
2247

2248
    if (options.isFull != null) {
120!
2249
      assert(typeof options.isFull === 'function');
120✔
2250
      this.isFull = options.isFull;
120✔
2251
    }
2252

2253
    if (options.createNonce != null) {
120!
2254
      assert(typeof options.createNonce === 'function');
120✔
2255
      this.createNonce = options.createNonce;
120✔
2256
    }
2257

2258
    if (options.hasNonce != null) {
120!
2259
      assert(typeof options.hasNonce === 'function');
120✔
2260
      this.hasNonce = options.hasNonce;
120✔
2261
    }
2262

2263
    if (options.getRate != null) {
120!
2264
      assert(typeof options.getRate === 'function');
120✔
2265
      this.getRate = options.getRate;
120✔
2266
    }
2267

2268
    return this;
120✔
2269
  }
2270

2271
  /**
2272
   * Instantiate options from object.
2273
   * @param {Object} options
2274
   * @returns {PeerOptions}
2275
   */
2276

2277
  static fromOptions(options) {
2278
    return new this().fromOptions(options);
×
2279
  }
2280

2281
  /**
2282
   * Get the chain height.
2283
   * @private
2284
   * @returns {Number}
2285
   */
2286

2287
  static getHeight() {
2288
    return 0;
×
2289
  }
2290

2291
  /**
2292
   * Test whether the chain is synced.
2293
   * @private
2294
   * @returns {Boolean}
2295
   */
2296

2297
  static isFull() {
2298
    return false;
×
2299
  }
2300

2301
  /**
2302
   * Create a version packet nonce.
2303
   * @private
2304
   * @param {String} hostname
2305
   * @returns {Buffer}
2306
   */
2307

2308
  static createNonce(hostname) {
2309
    return common.nonce();
×
2310
  }
2311

2312
  /**
2313
   * Test whether version nonce is ours.
2314
   * @private
2315
   * @param {Buffer} nonce
2316
   * @returns {Boolean}
2317
   */
2318

2319
  static hasNonce(nonce) {
2320
    return false;
×
2321
  }
2322

2323
  /**
2324
   * Get fee rate for txid.
2325
   * @private
2326
   * @param {Hash} hash
2327
   * @returns {Rate}
2328
   */
2329

2330
  static getRate(hash) {
2331
    return -1;
×
2332
  }
2333
}
2334

2335
/**
2336
 * Request Entry
2337
 * @ignore
2338
 */
2339

2340
class RequestEntry {
2341
  /**
2342
   * Create a request entry.
2343
   * @constructor
2344
   */
2345

2346
  constructor() {
2347
    this.timeout = 0;
713✔
2348
    this.jobs = [];
713✔
2349
  }
2350

2351
  addJob(resolve, reject) {
2352
    this.jobs.push({ resolve, reject });
180✔
2353
  }
2354

2355
  setTimeout(timeout) {
2356
    this.timeout = Date.now() + timeout;
1,065✔
2357
  }
2358

2359
  reject(err) {
2360
    for (const job of this.jobs)
2✔
2361
      job.reject(err);
×
2362

2363
    this.jobs.length = 0;
2✔
2364
  }
2365

2366
  resolve(result) {
2367
    for (const job of this.jobs)
270✔
2368
      job.resolve(result);
180✔
2369

2370
    this.jobs.length = 0;
270✔
2371
  }
2372
}
2373

2374
/*
2375
 * Expose
2376
 */
2377

2378
module.exports = Peer;
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

© 2025 Coveralls, Inc