net: use linked lists.
This commit is contained in:
parent
67ffecc989
commit
69a9b5873f
@ -327,7 +327,7 @@ RPC.prototype.getinfo = co(function* getinfo(args) {
|
||||
balance: Amount.btc(balance.unconfirmed, true),
|
||||
blocks: this.chain.height,
|
||||
timeoffset: this.network.time.offset,
|
||||
connections: this.pool.peers.all.length,
|
||||
connections: this.pool.peers.size(),
|
||||
proxy: '',
|
||||
difficulty: this._getDifficulty(),
|
||||
testnet: this.network.type !== Network.main,
|
||||
@ -379,7 +379,7 @@ RPC.prototype.getnetworkinfo = co(function* getnetworkinfo(args) {
|
||||
protocolversion: constants.VERSION,
|
||||
localservices: this.pool.services,
|
||||
timeoffset: this.network.time.offset,
|
||||
connections: this.pool.peers.all.length,
|
||||
connections: this.pool.peers.size(),
|
||||
networks: [],
|
||||
relayfee: Amount.btc(this.network.getMinRelay(), true),
|
||||
localaddresses: [],
|
||||
@ -413,7 +413,7 @@ RPC.prototype.addnode = co(function* addnode(args) {
|
||||
case 'onetry':
|
||||
if (!this.pool.peers.get(addr)) {
|
||||
peer = this.pool.createPeer(addr);
|
||||
this.pool.peers.addPending(peer);
|
||||
this.pool.peers.addOutbound(peer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -450,47 +450,46 @@ RPC.prototype.getaddednodeinfo = co(function* getaddednodeinfo(args) {
|
||||
peer = this.pool.peers.get(addr);
|
||||
if (!peer)
|
||||
throw new RPCError('Node has not been added.');
|
||||
peers = [peer];
|
||||
} else {
|
||||
peers = this.pool.peers.all;
|
||||
return [this._toAddedNode(peer)];
|
||||
}
|
||||
|
||||
for (i = 0; i < peers.length; i++) {
|
||||
peer = peers[i];
|
||||
out.push({
|
||||
addednode: peer.hostname,
|
||||
connected: peer.connected,
|
||||
addresses: [
|
||||
{
|
||||
address: peer.hostname,
|
||||
connected: peer.outbound
|
||||
? 'outbound'
|
||||
: 'inbound'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
for (peer = this.pool.peers.head(); peer; peer = peer.next)
|
||||
out.push(this._toAddedNode(peer));
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
RPC.prototype._toAddedNode = function _toAddedNode(peer) {
|
||||
return {
|
||||
addednode: peer.hostname,
|
||||
connected: peer.connected,
|
||||
addresses: [
|
||||
{
|
||||
address: peer.hostname,
|
||||
connected: peer.outbound
|
||||
? 'outbound'
|
||||
: 'inbound'
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
RPC.prototype.getconnectioncount = co(function* getconnectioncount(args) {
|
||||
if (args.help || args.length !== 0)
|
||||
throw new RPCError('getconnectioncount');
|
||||
|
||||
return this.pool.peers.all.length;
|
||||
return this.pool.peers.size();
|
||||
});
|
||||
|
||||
RPC.prototype.getnettotals = co(function* getnettotals(args) {
|
||||
var sent = 0;
|
||||
var recv = 0;
|
||||
var i, peer;
|
||||
var peer;
|
||||
|
||||
if (args.help || args.length > 0)
|
||||
throw new RPCError('getnettotals');
|
||||
|
||||
for (i = 0; i < this.pool.peers.all.length; i++) {
|
||||
peer = this.pool.peers.all[i];
|
||||
for (peer = this.pool.peers.head(); peer; peer = peer.next) {
|
||||
sent += peer.socket.bytesWritten;
|
||||
recv += peer.socket.bytesRead;
|
||||
}
|
||||
@ -504,13 +503,12 @@ RPC.prototype.getnettotals = co(function* getnettotals(args) {
|
||||
|
||||
RPC.prototype.getpeerinfo = co(function* getpeerinfo(args) {
|
||||
var peers = [];
|
||||
var i, peer;
|
||||
var peer;
|
||||
|
||||
if (args.help || args.length !== 0)
|
||||
throw new RPCError('getpeerinfo');
|
||||
|
||||
for (i = 0; i < this.pool.peers.all.length; i++) {
|
||||
peer = this.pool.peers.all[i];
|
||||
for (peer = this.pool.peers.head(); peer; peer = peer.next) {
|
||||
peers.push({
|
||||
id: peer.id,
|
||||
addr: peer.hostname,
|
||||
@ -538,13 +536,11 @@ RPC.prototype.getpeerinfo = co(function* getpeerinfo(args) {
|
||||
});
|
||||
|
||||
RPC.prototype.ping = co(function* ping(args) {
|
||||
var i;
|
||||
|
||||
if (args.help || args.length !== 0)
|
||||
throw new RPCError('ping');
|
||||
|
||||
for (i = 0; i < this.pool.peers.all.length; i++)
|
||||
this.pool.peers.all[i].sendPing();
|
||||
for (peer = this.pool.peers.head(); peer; peer = peer.next)
|
||||
peer.sendPing();
|
||||
|
||||
return null;
|
||||
});
|
||||
@ -1538,7 +1534,7 @@ RPC.prototype.getblocktemplate = co(function* getblocktemplate(args) {
|
||||
}
|
||||
|
||||
if (!this.network.selfConnect) {
|
||||
if (this.pool.peers.all.length === 0)
|
||||
if (this.pool.peers.size() === 0)
|
||||
throw new RPCError('Bitcoin is not connected!');
|
||||
|
||||
if (!this.chain.isFull())
|
||||
|
||||
@ -631,7 +631,6 @@ HTTPServer.prototype._init = function _init() {
|
||||
this.get('/', function(req, res, send, next) {
|
||||
var totalTX = this.mempool ? this.mempool.totalTX : 0;
|
||||
var size = this.mempool ? this.mempool.getSize() : 0;
|
||||
var loader = this.pool.peers.load ? 1 : 0;
|
||||
|
||||
send(200, {
|
||||
version: constants.USER_VERSION,
|
||||
@ -644,9 +643,9 @@ HTTPServer.prototype._init = function _init() {
|
||||
},
|
||||
pool: {
|
||||
services: this.pool.services.toString(2),
|
||||
outbound: this.pool.peers.outbound.length + loader,
|
||||
pending: this.pool.peers.pending.length,
|
||||
inbound: this.pool.peers.inbound.length
|
||||
outbound: this.pool.peers.outbound,
|
||||
pending: this.pool.peers.pending,
|
||||
inbound: this.pool.peers.inbound
|
||||
},
|
||||
mempool: {
|
||||
tx: totalTX,
|
||||
|
||||
@ -26,6 +26,7 @@ var BIP152 = require('./bip152');
|
||||
var Block = require('../primitives/block');
|
||||
var TX = require('../primitives/tx');
|
||||
var errors = require('../btc/errors');
|
||||
var List = require('../utils/list');
|
||||
var packetTypes = packets.types;
|
||||
var VerifyResult = errors.VerifyResult;
|
||||
|
||||
@ -93,6 +94,7 @@ function Peer(pool, addr, socket) {
|
||||
this.version = null;
|
||||
this.destroyed = false;
|
||||
this.ack = false;
|
||||
this.pending = true;
|
||||
this.connected = false;
|
||||
this.ts = 0;
|
||||
this.preferHeaders = false;
|
||||
@ -119,6 +121,9 @@ function Peer(pool, addr, socket) {
|
||||
this.drainSize = 0;
|
||||
this.drainQueue = [];
|
||||
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
|
||||
this.challenge = null;
|
||||
this.lastPong = -1;
|
||||
this.lastPing = -1;
|
||||
@ -134,8 +139,8 @@ function Peer(pool, addr, socket) {
|
||||
this.requestTimeout = 10000;
|
||||
this.requestMap = {};
|
||||
|
||||
this.queueBlock = [];
|
||||
this.queueTX = [];
|
||||
this.queueBlock = new List();
|
||||
this.queueTX = new List();
|
||||
|
||||
this.uid = 0;
|
||||
this.id = Peer.uid++;
|
||||
@ -154,6 +159,7 @@ function Peer(pool, addr, socket) {
|
||||
} else {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.pending = false;
|
||||
}
|
||||
|
||||
if (this.options.bip151) {
|
||||
@ -491,7 +497,7 @@ Peer.prototype._finalize = co(function* _finalize() {
|
||||
yield this.updateWatch();
|
||||
|
||||
// Announce our currently broadcasted items.
|
||||
yield this.announce(this.pool.invItems);
|
||||
yield this.announce(this.pool.invItems.toArray());
|
||||
|
||||
// Set a fee rate filter.
|
||||
if (this.pool.feeRate !== -1)
|
||||
@ -1883,7 +1889,7 @@ Peer.prototype._handleAddr = function _handleAddr(packet) {
|
||||
'Received %d addrs (hosts=%d, peers=%d) (%s).',
|
||||
addrs.length,
|
||||
this.pool.hosts.items.length,
|
||||
this.pool.peers.all.length,
|
||||
this.pool.peers.size(),
|
||||
this.hostname);
|
||||
|
||||
this.fire('addr', addrs);
|
||||
|
||||
293
lib/net/pool.js
293
lib/net/pool.js
@ -29,6 +29,7 @@ var tcp = require('./tcp');
|
||||
var request = require('../http/request');
|
||||
var VerifyError = errors.VerifyError;
|
||||
var VerifyResult = errors.VerifyResult;
|
||||
var List = require('../utils/list');
|
||||
|
||||
/**
|
||||
* A pool of peers for handling all network activity.
|
||||
@ -145,7 +146,7 @@ function Pool(options) {
|
||||
|
||||
// Currently broadcasted objects.
|
||||
this.invMap = {};
|
||||
this.invItems = [];
|
||||
this.invItems = new List();
|
||||
this.invTimeout = 60000;
|
||||
|
||||
this.scheduled = false;
|
||||
@ -327,14 +328,14 @@ Pool.prototype._open = co(function* _open() {
|
||||
*/
|
||||
|
||||
Pool.prototype._close = co(function* close() {
|
||||
var i, items, hashes, hash;
|
||||
var i, next, item, hashes, hash;
|
||||
|
||||
this.stopSync();
|
||||
|
||||
items = this.invItems.slice();
|
||||
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i].finish();
|
||||
for (item = this.invItems.head; item; item = next) {
|
||||
next = item.next;
|
||||
item.finish();
|
||||
}
|
||||
|
||||
hashes = Object.keys(this.requestMap);
|
||||
|
||||
@ -467,7 +468,7 @@ Pool.prototype._handleLeech = function _handleLeech(socket) {
|
||||
|
||||
addr = NetworkAddress.fromSocket(socket, this.network);
|
||||
|
||||
if (this.peers.inbound.length >= this.maxInbound) {
|
||||
if (this.peers.inbound >= this.maxInbound) {
|
||||
this.logger.debug('Ignoring leech: too many inbound (%s).', addr.hostname);
|
||||
socket.destroy();
|
||||
return;
|
||||
@ -613,7 +614,9 @@ Pool.prototype.addLoader = function addLoader() {
|
||||
|
||||
this.logger.info('Added loader peer (%s).', peer.hostname);
|
||||
|
||||
this.peers.addLoader(peer);
|
||||
assert(!this.peers.load);
|
||||
this.peers.load = peer;
|
||||
this.peers.add(peer);
|
||||
this.fillPeers();
|
||||
|
||||
util.nextTick(function() {
|
||||
@ -675,13 +678,13 @@ Pool.prototype.startSync = function startSync() {
|
||||
*/
|
||||
|
||||
Pool.prototype.sync = function sync() {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.trySync();
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].trySync();
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.trySync();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -690,15 +693,11 @@ Pool.prototype.sync = function sync() {
|
||||
*/
|
||||
|
||||
Pool.prototype.forceSync = function forceSync() {
|
||||
var i, peer;
|
||||
var peer;
|
||||
|
||||
if (this.peers.load) {
|
||||
this.peers.load.syncSent = false;
|
||||
this.peers.load.trySync();
|
||||
}
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++) {
|
||||
peer = this.peers.outbound[i];
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.syncSent = false;
|
||||
peer.trySync();
|
||||
}
|
||||
@ -709,7 +708,7 @@ Pool.prototype.forceSync = function forceSync() {
|
||||
*/
|
||||
|
||||
Pool.prototype.stopSync = function stopSync() {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (!this.syncing)
|
||||
return;
|
||||
@ -722,11 +721,11 @@ Pool.prototype.stopSync = function stopSync() {
|
||||
this.stopInterval();
|
||||
this.stopTimeout();
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.syncSent = false;
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].syncSent = false;
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.syncSent = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -991,9 +990,9 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
|
||||
this.chain.total,
|
||||
this.chain.orphan.count,
|
||||
this.activeBlocks,
|
||||
peer.queueBlock.length,
|
||||
peer.queueBlock.size,
|
||||
block.bits,
|
||||
this.peers.all.length,
|
||||
this.peers.size(),
|
||||
this.chain.locker.pending.length,
|
||||
this.chain.locker.jobs.length);
|
||||
}
|
||||
@ -1011,13 +1010,13 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
|
||||
*/
|
||||
|
||||
Pool.prototype.sendMempool = function sendMempool() {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.sendMempool();
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].sendMempool();
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.sendMempool();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1026,16 +1025,10 @@ Pool.prototype.sendMempool = function sendMempool() {
|
||||
*/
|
||||
|
||||
Pool.prototype.sendAlert = function sendAlert(alert) {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.sendAlert(alert);
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].sendAlert(alert);
|
||||
|
||||
for (i = 0; i < this.peers.inbound.length; i++)
|
||||
this.peers.inbound[i].sendAlert(alert);
|
||||
for (peer = this.peers.head(); peer; peer = peer.next)
|
||||
peer.sendAlert(alert);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1077,7 +1070,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
|
||||
self.stopInterval();
|
||||
self.stopTimeout();
|
||||
|
||||
if (self.peers.size() === 0) {
|
||||
if (self.peers.outbound === 0) {
|
||||
self.logger.warning('%s %s %s',
|
||||
'Could not connect to any peers.',
|
||||
'Do you have a network connection?',
|
||||
@ -1415,7 +1408,7 @@ Pool.prototype.addLeech = function addLeech(addr, socket) {
|
||||
|
||||
this.logger.info('Added leech peer (%s).', peer.hostname);
|
||||
|
||||
this.peers.addLeech(peer);
|
||||
this.peers.add(peer);
|
||||
|
||||
util.nextTick(function() {
|
||||
self.emit('leech', peer);
|
||||
@ -1435,7 +1428,7 @@ Pool.prototype.addPeer = function addPeer() {
|
||||
if (!this.loaded)
|
||||
return;
|
||||
|
||||
if (this.peers.isFull())
|
||||
if (this.peers.outbound >= this.maxOutbound)
|
||||
return;
|
||||
|
||||
// Hang back if we don't have a loader peer yet.
|
||||
@ -1449,7 +1442,7 @@ Pool.prototype.addPeer = function addPeer() {
|
||||
|
||||
peer = this.createPeer(addr);
|
||||
|
||||
this.peers.addPending(peer);
|
||||
this.peers.add(peer);
|
||||
|
||||
util.nextTick(function() {
|
||||
self.emit('peer', peer);
|
||||
@ -1465,7 +1458,7 @@ Pool.prototype.fillPeers = function fillPeers() {
|
||||
var i;
|
||||
|
||||
this.logger.debug('Refilling peers (%d/%d).',
|
||||
this.peers.all.length - this.peers.inbound.length,
|
||||
this.peers.outbound,
|
||||
this.maxOutbound);
|
||||
|
||||
for (i = 0; i < this.maxOutbound - 1; i++)
|
||||
@ -1541,19 +1534,18 @@ Pool.prototype.unwatch = function unwatch() {
|
||||
|
||||
Pool.prototype.updateWatch = function updateWatch() {
|
||||
var self = this;
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (this.pendingWatch != null)
|
||||
return;
|
||||
|
||||
this.pendingWatch = setTimeout(function() {
|
||||
self.pendingWatch = null;
|
||||
|
||||
if (self.peers.load)
|
||||
self.peers.load.updateWatch();
|
||||
|
||||
for (i = 0; i < self.peers.outbound.length; i++)
|
||||
self.peers.outbound[i].updateWatch();
|
||||
for (peer = self.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.updateWatch();
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
@ -1634,16 +1626,15 @@ Pool.prototype.getTX = function getTX(peer, hash) {
|
||||
|
||||
item = new LoadRequest(this, peer, this.txType, hash);
|
||||
|
||||
if (peer.queueTX.length === 0) {
|
||||
if (peer.queueTX.size === 0) {
|
||||
util.nextTick(function() {
|
||||
self.logger.debug(
|
||||
'Requesting %d/%d txs from peer with getdata (%s).',
|
||||
peer.queueTX.length,
|
||||
peer.queueTX.size,
|
||||
self.activeTX,
|
||||
peer.hostname);
|
||||
|
||||
peer.getData(peer.queueTX);
|
||||
peer.queueTX.length = 0;
|
||||
peer.getData(peer.queueTX.slice());
|
||||
});
|
||||
}
|
||||
|
||||
@ -1709,29 +1700,29 @@ Pool.prototype.scheduleRequests = co(function* scheduleRequests(peer) {
|
||||
*/
|
||||
|
||||
Pool.prototype.sendRequests = function sendRequests(peer) {
|
||||
var i, size, items;
|
||||
var i, size, items, item;
|
||||
|
||||
if (peer.queueBlock.length === 0)
|
||||
if (peer.queueBlock.size === 0)
|
||||
return;
|
||||
|
||||
if (this.options.spv) {
|
||||
if (this.activeBlocks >= 500)
|
||||
if (this.activeBlocks >= 2000)
|
||||
return;
|
||||
|
||||
items = peer.queueBlock.slice();
|
||||
peer.queueBlock.length = 0;
|
||||
size = peer.queueBlock.size;
|
||||
} else {
|
||||
size = this.network.getBatchSize(this.chain.height);
|
||||
|
||||
if (this.activeBlocks >= size)
|
||||
return;
|
||||
|
||||
items = peer.queueBlock.slice(0, size);
|
||||
peer.queueBlock = peer.queueBlock.slice(size);
|
||||
}
|
||||
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i] = items[i].start();
|
||||
items = peer.queueBlock.slice(size);
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
item.start();
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
'Requesting %d/%d blocks from peer with getdata (%s).',
|
||||
@ -1799,13 +1790,13 @@ Pool.prototype.broadcast = function broadcast(msg) {
|
||||
*/
|
||||
|
||||
Pool.prototype.announce = function announce(msg) {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.tryAnnounce(msg);
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].tryAnnounce(msg);
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.tryAnnounce(msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1814,15 +1805,15 @@ Pool.prototype.announce = function announce(msg) {
|
||||
*/
|
||||
|
||||
Pool.prototype.setFeeRate = function setFeeRate(rate) {
|
||||
var i;
|
||||
var peer;
|
||||
|
||||
this.feeRate = rate;
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.sendFeeRate(rate);
|
||||
|
||||
for (i = 0; i < this.peers.outbound.length; i++)
|
||||
this.peers.outbound[i].sendFeeRate(rate);
|
||||
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||
if (!peer.outbound || peer.pending)
|
||||
continue;
|
||||
peer.sendFeeRate(rate);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1978,52 +1969,48 @@ Pool.prototype.getIP2 = co(function* getIP2() {
|
||||
|
||||
function PeerList(pool) {
|
||||
this.pool = pool;
|
||||
// Peers that are loading blocks themselves
|
||||
this.outbound = [];
|
||||
// Peers that are still connecting
|
||||
this.pending = [];
|
||||
// Peers that connected to us
|
||||
this.inbound = [];
|
||||
// Peers that are loading block ids
|
||||
this.load = null;
|
||||
// All peers
|
||||
this.all = [];
|
||||
// Map of hostnames
|
||||
this.map = {};
|
||||
this.list = new List();
|
||||
this.load = null;
|
||||
this.inbound = 0;
|
||||
this.outbound = 0;
|
||||
this.pending = 0;
|
||||
}
|
||||
|
||||
PeerList.prototype.addLoader = function addLoader(peer) {
|
||||
this.load = peer;
|
||||
this.all.push(peer);
|
||||
assert(!this.map[peer.hostname]);
|
||||
this.map[peer.hostname] = peer;
|
||||
PeerList.prototype.head = function head() {
|
||||
return this.list.head;
|
||||
};
|
||||
|
||||
PeerList.prototype.addPending = function addPending(peer) {
|
||||
this.pending.push(peer);
|
||||
this.all.push(peer);
|
||||
assert(!this.map[peer.hostname]);
|
||||
this.map[peer.hostname] = peer;
|
||||
PeerList.prototype.tail = function tail() {
|
||||
return this.list.tail;
|
||||
};
|
||||
|
||||
PeerList.prototype.addLeech = function addLeech(peer) {
|
||||
this.inbound.push(peer);
|
||||
this.all.push(peer);
|
||||
assert(!this.map[peer.hostname]);
|
||||
this.map[peer.hostname] = peer;
|
||||
PeerList.prototype.size = function size() {
|
||||
return this.list.size;
|
||||
};
|
||||
|
||||
PeerList.prototype.promote = function promote(peer) {
|
||||
if (util.binaryRemove(this.pending, peer, compare))
|
||||
util.binaryInsert(this.outbound, peer, compare);
|
||||
assert(peer.outbound);
|
||||
assert(peer.pending);
|
||||
peer.pending = false;
|
||||
this.pending--;
|
||||
};
|
||||
|
||||
PeerList.prototype.add = function add(peer) {
|
||||
assert(this.list.push(peer));
|
||||
assert(!this.map[peer.hostname]);
|
||||
this.map[peer.hostname] = peer;
|
||||
if (peer.outbound) {
|
||||
this.outbound++;
|
||||
if (peer.pending)
|
||||
this.pending++;
|
||||
} else {
|
||||
this.inbound++;
|
||||
}
|
||||
};
|
||||
|
||||
PeerList.prototype.remove = function remove(peer) {
|
||||
util.binaryRemove(this.pending, peer, compare);
|
||||
util.binaryRemove(this.outbound, peer, compare);
|
||||
util.binaryRemove(this.inbound, peer, compare);
|
||||
util.binaryRemove(this.all, peer, compare);
|
||||
|
||||
assert(this.list.remove(peer));
|
||||
assert(this.map[peer.hostname]);
|
||||
delete this.map[peer.hostname];
|
||||
|
||||
@ -2031,66 +2018,38 @@ PeerList.prototype.remove = function remove(peer) {
|
||||
this.pool.logger.info('Removed loader peer (%s).', peer.hostname);
|
||||
this.load = null;
|
||||
}
|
||||
};
|
||||
|
||||
PeerList.prototype.demoteLoader = function demoteLoader() {
|
||||
var peer = this.load;
|
||||
assert(peer);
|
||||
this.load = null;
|
||||
if (peer.ack)
|
||||
util.binaryInsert(this.outbound, peer, compare);
|
||||
else
|
||||
util.binaryInsert(this.pending, peer, compare);
|
||||
if (peer.outbound) {
|
||||
this.outbound--;
|
||||
if (peer.pending)
|
||||
this.pending--;
|
||||
} else {
|
||||
this.inbound--;
|
||||
}
|
||||
};
|
||||
|
||||
PeerList.prototype.repurpose = function repurpose(peer) {
|
||||
var r1, r2;
|
||||
|
||||
assert(peer.outbound);
|
||||
|
||||
if (this.load)
|
||||
this.demoteLoader();
|
||||
|
||||
r1 = util.binaryRemove(this.pending, peer, compare);
|
||||
r2 = util.binaryRemove(this.outbound, peer, compare);
|
||||
|
||||
assert(r1 || r2);
|
||||
|
||||
this.load = peer;
|
||||
};
|
||||
|
||||
PeerList.prototype.isFull = function isFull() {
|
||||
return this.size() >= this.pool.maxOutbound - 1;
|
||||
};
|
||||
|
||||
PeerList.prototype.size = function size() {
|
||||
return this.outbound.length + this.pending.length;
|
||||
};
|
||||
|
||||
PeerList.prototype.get = function get(addr) {
|
||||
return this.map[addr.hostname];
|
||||
};
|
||||
|
||||
PeerList.prototype.destroy = function destroy() {
|
||||
var i, peers;
|
||||
var peer, next;
|
||||
|
||||
if (this.load)
|
||||
this.load.destroy();
|
||||
this.map = {};
|
||||
this.load = null;
|
||||
this.inbound = 0;
|
||||
this.outbound = 0;
|
||||
this.pending = 0;
|
||||
|
||||
peers = this.outbound.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.pending.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.inbound.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
for (peer = this.list.head; peer; peer = next) {
|
||||
next = peer.next;
|
||||
peer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2341,10 +2300,12 @@ function LoadRequest(pool, peer, type, hash) {
|
||||
this.type = type;
|
||||
this.hash = hash;
|
||||
this.active = false;
|
||||
this.id = this.pool.uid++;
|
||||
this.timeout = null;
|
||||
this.onTimeout = this._onTimeout.bind(this);
|
||||
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
|
||||
assert(!this.pool.requestMap[this.hash]);
|
||||
this.pool.requestMap[this.hash] = this;
|
||||
}
|
||||
@ -2408,9 +2369,9 @@ LoadRequest.prototype.finish = function finish() {
|
||||
}
|
||||
|
||||
if (this.type === this.pool.txType)
|
||||
util.binaryRemove(this.peer.queueTX, this, compare);
|
||||
this.peer.queueTX.remove(this);
|
||||
else
|
||||
util.binaryRemove(this.peer.queueBlock, this, compare);
|
||||
this.peer.queueBlock.remove(this);
|
||||
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout);
|
||||
@ -2502,7 +2463,7 @@ BroadcastItem.prototype.start = function start() {
|
||||
assert(!this.pool.invMap[this.hash], 'Already started.');
|
||||
|
||||
this.pool.invMap[this.hash] = this;
|
||||
util.binaryInsert(this.pool.invItems, this, compare);
|
||||
assert(this.pool.invItems.push(this));
|
||||
|
||||
this.refresh();
|
||||
|
||||
@ -2550,7 +2511,7 @@ BroadcastItem.prototype.finish = function finish(err) {
|
||||
this.timeout = null;
|
||||
|
||||
delete this.pool.invMap[this.hash];
|
||||
util.binaryRemove(this.pool.invItems, this, compare);
|
||||
assert(this.pool.invItems.remove(this));
|
||||
|
||||
for (i = 0; i < this.callback.length; i++)
|
||||
this.callback[i](err);
|
||||
|
||||
237
lib/utils/list.js
Normal file
237
lib/utils/list.js
Normal file
@ -0,0 +1,237 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
/**
|
||||
* A linked list.
|
||||
* @exports List
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function List() {
|
||||
if (!(this instanceof List))
|
||||
return new List();
|
||||
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cache. Clear all items.
|
||||
*/
|
||||
|
||||
List.prototype.reset = function reset() {
|
||||
var item, next;
|
||||
|
||||
for (item = this.head; item; item = next) {
|
||||
next = item.next;
|
||||
item.prev = null;
|
||||
item.next = null;
|
||||
}
|
||||
|
||||
assert(!item);
|
||||
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
this.size = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the first item in the list.
|
||||
*/
|
||||
|
||||
List.prototype.shift = function shift() {
|
||||
var item = this.head;
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
this.remove(item);
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepend an item to the linked list (sets new head).
|
||||
* @private
|
||||
* @param {ListItem}
|
||||
*/
|
||||
|
||||
List.prototype.unshift = function unshift(item) {
|
||||
return this.insert(null, item);
|
||||
};
|
||||
|
||||
/**
|
||||
* Append an item to the linked list (sets new tail).
|
||||
* @private
|
||||
* @param {ListItem}
|
||||
*/
|
||||
|
||||
List.prototype.push = function push(item) {
|
||||
return this.insert(this.tail, item);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the last item in the list.
|
||||
*/
|
||||
|
||||
List.prototype.pop = function pop() {
|
||||
var item = this.tail;
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
this.remove(item);
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert item into the linked list.
|
||||
* @private
|
||||
* @param {ListItem|null} ref
|
||||
* @param {ListItem} item
|
||||
*/
|
||||
|
||||
List.prototype.insert = function insert(ref, item) {
|
||||
if (item.prev || item.next || item === this.head)
|
||||
return false;
|
||||
|
||||
assert(!item.prev);
|
||||
assert(!item.next);
|
||||
|
||||
if (ref == null) {
|
||||
if (!this.head) {
|
||||
this.head = item;
|
||||
this.tail = item;
|
||||
} else {
|
||||
this.head.prev = item;
|
||||
item.next = this.head;
|
||||
this.head = item;
|
||||
}
|
||||
this.size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
item.next = ref.next;
|
||||
item.prev = ref;
|
||||
ref.next = item;
|
||||
|
||||
if (ref === this.tail)
|
||||
this.tail = item;
|
||||
|
||||
this.size++;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove item from the linked list.
|
||||
* @private
|
||||
* @param {ListItem}
|
||||
*/
|
||||
|
||||
List.prototype.remove = function remove(item) {
|
||||
if (!item.prev && !item.next && item !== this.head)
|
||||
return false;
|
||||
|
||||
if (item.prev)
|
||||
item.prev.next = item.next;
|
||||
|
||||
if (item.next)
|
||||
item.next.prev = item.prev;
|
||||
|
||||
if (item === this.head)
|
||||
this.head = item.next;
|
||||
|
||||
if (item === this.tail)
|
||||
this.tail = item.prev || this.head;
|
||||
|
||||
if (!this.head)
|
||||
assert(!this.tail);
|
||||
|
||||
if (!this.tail)
|
||||
assert(!this.head);
|
||||
|
||||
item.prev = null;
|
||||
item.next = null;
|
||||
|
||||
this.size--;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice the list to an array of items.
|
||||
* @param {Number} total
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
|
||||
List.prototype.slice = function slice(total) {
|
||||
var items = [];
|
||||
var item, next;
|
||||
|
||||
if (total == null)
|
||||
total = Infinity;
|
||||
|
||||
for (item = this.head; item; item = next) {
|
||||
next = item.next;
|
||||
item.prev = null;
|
||||
item.next = null;
|
||||
|
||||
this.size--;
|
||||
|
||||
items.push(item);
|
||||
|
||||
if (items.length === total)
|
||||
break;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
this.head = next;
|
||||
next.prev = null;
|
||||
} else {
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the list to an array of items.
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
|
||||
List.prototype.toArray = function toArray() {
|
||||
var items = [];
|
||||
var item;
|
||||
|
||||
for (item = this.head; item; item = item.next)
|
||||
items.push(item);
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an LRU item.
|
||||
* @constructor
|
||||
* @private
|
||||
* @param {String} key
|
||||
* @param {Object} value
|
||||
*/
|
||||
|
||||
function ListItem(value) {
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
exports = List;
|
||||
exports.Item = ListItem;
|
||||
|
||||
module.exports = exports;
|
||||
Loading…
Reference in New Issue
Block a user