peer/pool: add origin satoshi protocol.
This commit is contained in:
parent
a6584ae821
commit
feda74523f
@ -37,6 +37,18 @@ function Chain(options) {
|
||||
};
|
||||
this.request = new utils.RequestCache();
|
||||
|
||||
// Start from the genesis block
|
||||
// if we're using the original protocol.
|
||||
if (!this.options.relay) {
|
||||
preload = {
|
||||
v: preload.v,
|
||||
type: preload.type,
|
||||
hashes: preload.hashes.slice(0, 1),
|
||||
ts: preload.ts.slice(0, 1),
|
||||
heights: preload.heights.slice(0, 1)
|
||||
};
|
||||
}
|
||||
|
||||
this.fromJSON(preload);
|
||||
|
||||
// Last TS after preload, needed for fill percent
|
||||
@ -198,6 +210,31 @@ Chain.prototype.add = function add(block) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.options.relay) {
|
||||
var hash = block.hash('hex');
|
||||
var prev = block.prevBlock;
|
||||
var prevProbe = this._probeIndex(prev, block.ts);
|
||||
if (prevProbe) {
|
||||
this._addIndex(hash, block.ts, prevProbe.height + 1);
|
||||
if (this.orphan.map[hash]) {
|
||||
delete this.orphan.map[hash];
|
||||
this.orphan.count--;
|
||||
}
|
||||
this.block.list.push(block);
|
||||
this._bloomBlock(block);
|
||||
this.request.fullfill(hash, block);
|
||||
this._compress();
|
||||
return true;
|
||||
} else {
|
||||
if (!this.orphan.map[prev]) {
|
||||
this.orphan.map[prev] = block;
|
||||
this.orphan.count++;
|
||||
}
|
||||
this._compress();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var res = false;
|
||||
var initial = block;
|
||||
do {
|
||||
@ -388,6 +425,20 @@ Chain.prototype.getLast = function getLast(cb) {
|
||||
return cb(this.index.hashes[this.index.hashes.length - 1]);
|
||||
};
|
||||
|
||||
Chain.prototype.getStartHeight = function getLast(cb) {
|
||||
if (this.options.relay) {
|
||||
if (this.options.startHeight != null) {
|
||||
return this.options.startHeight;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
for (var i = 0; i < this.index.heights.length; i++) {
|
||||
if (this.index.heights[i + 1] !== this.index.heights[i] + 1) {
|
||||
return this.index.heights[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Chain.prototype.toJSON = function toJSON() {
|
||||
var keep = 1000;
|
||||
|
||||
|
||||
@ -89,6 +89,14 @@ Peer.prototype._init = function init() {
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
if (!this.pool.options.relay) {
|
||||
this.once('version', function() {
|
||||
var ip = self.socket && self.socket.remoteAddress || '0.0.0.0';
|
||||
self.pool.emit('debug', 'version (%s): loading locator hashes for getblocks', ip);
|
||||
self.loadBlocks(self.pool.locatorHashes(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
this._ping.timer = setInterval(function() {
|
||||
self._write(self.framer.ping([
|
||||
0xde, 0xad, 0xbe, 0xef,
|
||||
@ -98,9 +106,7 @@ Peer.prototype._init = function init() {
|
||||
|
||||
// Send hello
|
||||
this._write(this.framer.version({
|
||||
height: this.options.startHeight != null
|
||||
? this.options.startHeight
|
||||
: 0,
|
||||
height: this.pool.chain.getStartHeight(),
|
||||
relay: this.options.relay
|
||||
}));
|
||||
|
||||
@ -167,18 +173,8 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
};
|
||||
|
||||
Peer.prototype.updateWatch = function updateWatch() {
|
||||
if (!this.pool.options.relay) {
|
||||
if (this.ack) {
|
||||
var self = this;
|
||||
if (this.pool.block.lastHash)
|
||||
this.loadBlocks([ self.pool.block.lastHash ], 0);
|
||||
else
|
||||
this.pool.chain.getLast(function(hash) {
|
||||
self.loadBlocks([ hash ], 0);
|
||||
});
|
||||
}
|
||||
if (!this.pool.options.relay)
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ack)
|
||||
this._write(this.framer.filterLoad(this.bloom, 'none'));
|
||||
@ -438,11 +434,57 @@ Peer.prototype._handleInv = function handleInv(items) {
|
||||
this.emit('blocks', blocks);
|
||||
|
||||
if (!this.pool.options.relay) {
|
||||
if (txs.length)
|
||||
var self = this;
|
||||
|
||||
if (txs.length) {
|
||||
this.emit('txs', txs.map(function(tx) {
|
||||
return tx.hash;
|
||||
}));
|
||||
this.getData(items);
|
||||
// this.getData(txs);
|
||||
}
|
||||
|
||||
var hashes = blocks.map(utils.toHex);
|
||||
|
||||
var orphans = Object.keys(this.chain.orphan.map).reduce(function(out, prev) {
|
||||
var orphan = self.chain.orphan.map[prev];
|
||||
out[orphan.hash('hex')] = true;
|
||||
return out;
|
||||
}, {});
|
||||
|
||||
hashes = hashes.filter(function(hash) {
|
||||
return !orphans[hash];
|
||||
});
|
||||
|
||||
// hashes = hashes.filter(function(hash) {
|
||||
// return !self.chain.index.bloom.test(hash, 'hex')
|
||||
// && !self.chain.block.bloom.test(hash, 'hex');
|
||||
// });
|
||||
|
||||
if (blocks.length === 1) {
|
||||
var ip = this.socket && this.socket.remoteAddress || '0.0.0.0';
|
||||
this.pool.emit('debug', 'inv (%s): %s', ip, utils.revHex(utils.toHex(blocks[0])));
|
||||
}
|
||||
|
||||
if (!hashes.length) {
|
||||
var ip = this.socket && this.socket.remoteAddress || '0.0.0.0';
|
||||
//if (blocks.length === 1 && this._latestBlock) {
|
||||
if (blocks.length === 1) {
|
||||
// Already have the latest orphan, another getblocks:
|
||||
this.pool.emit('debug', 'inv (%s): loading locator hashes for getblocks', ip);
|
||||
this.loadBlocks(this.pool.locatorHashes(), 0);
|
||||
} else {
|
||||
this.pool.emit('debug', 'inv (%s): no hashes to getdata', ip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.getData(hashes.map(function(hash) {
|
||||
return {
|
||||
type: 'block',
|
||||
hash: hash
|
||||
};
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,9 @@ function Pool(options) {
|
||||
storage: this.storage,
|
||||
// Since regular blocks contain transactions and full merkle
|
||||
// trees, it's risky to cache 2000 blocks. Let's do 100.
|
||||
cacheLimit: !this.options.relay ? 100 : null
|
||||
cacheLimit: !this.options.relay ? 100 : null,
|
||||
relay: this.options.relay,
|
||||
startHeight: this.options.startHeight
|
||||
});
|
||||
this.watchMap = {};
|
||||
this.bloom = new bcoin.bloom(8 * 1024,
|
||||
@ -98,10 +100,41 @@ Pool.prototype._init = function _init() {
|
||||
|
||||
var self = this;
|
||||
this.chain.on('missing', function(hash, preload, parent) {
|
||||
if (!self.options.relay) return;
|
||||
self._request('block', hash, { force: true });
|
||||
self._scheduleRequests();
|
||||
self._loadRange(preload);
|
||||
});
|
||||
|
||||
if (!this.options.relay) {
|
||||
// Non-standard stall recovery,
|
||||
// this shouldn't be necessary.
|
||||
this._lastLocator = null;
|
||||
setInterval(function() {
|
||||
if (!self._lastLocator) return;
|
||||
if (Date.now() - 60 * 1000 > self._lastLocator) {
|
||||
self.emit('debug', 'invoking stall recovery!');
|
||||
[].concat(
|
||||
self.peers.pending,
|
||||
self.peers.block,
|
||||
self.peers.load
|
||||
).reduce(function(out, peer) {
|
||||
if (peer && !~out.indexOf(peer)) {
|
||||
out.push(peer);
|
||||
}
|
||||
return out;
|
||||
}, []).forEach(function(peer) {
|
||||
var ip = peer.socket && peer.socket.remoteAddress || '0.0.0.0';
|
||||
self.emit('debug', 'invoking stall recovery on peer (%s)!', ip);
|
||||
peer.loadBlocks(self.locatorHashes(), 0);
|
||||
});
|
||||
} else {
|
||||
self.emit('debug', 'stall recovery diff: %d/%d',
|
||||
self._lastLocator / 1000 | 0,
|
||||
Date.now() / 1000 | 0);
|
||||
}
|
||||
}, 10 * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
Pool.prototype._addLoader = function _addLoader() {
|
||||
@ -157,6 +190,8 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
|
||||
// Split blocks and request them using multiple peers
|
||||
peer.on('blocks', function(hashes) {
|
||||
if (!self.options.relay) return;
|
||||
|
||||
if (hashes.length === 0) {
|
||||
// Reset global load
|
||||
self.block.lastHash = null;
|
||||
@ -198,6 +233,8 @@ Pool.prototype.isFull = function isFull() {
|
||||
};
|
||||
|
||||
Pool.prototype._loadRange = function _loadRange(hashes, force) {
|
||||
if (!this.options.relay) return;
|
||||
|
||||
if (!hashes)
|
||||
return;
|
||||
|
||||
@ -220,6 +257,8 @@ Pool.prototype._loadRange = function _loadRange(hashes, force) {
|
||||
};
|
||||
|
||||
Pool.prototype._load = function _load() {
|
||||
if (!this.options.relay) return;
|
||||
|
||||
if (this.request.queue.length >= this.load.hwm) {
|
||||
this.load.hiReached = true;
|
||||
return false;
|
||||
@ -295,19 +334,34 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
self._scheduleRequests();
|
||||
});
|
||||
|
||||
peer.on('merkleblock', function(block) {
|
||||
// Reset backoff, peer seems to be responsive
|
||||
backoff = 0;
|
||||
if (this.options.relay) {
|
||||
peer.on('merkleblock', function(block) {
|
||||
// Reset backoff, peer seems to be responsive
|
||||
backoff = 0;
|
||||
|
||||
self._response(block);
|
||||
self.chain.add(block);
|
||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||
self.emit('block', block, peer);
|
||||
});
|
||||
|
||||
if (!this.options.relay) {
|
||||
self._response(block);
|
||||
self.chain.add(block);
|
||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||
self.emit('block', block, peer);
|
||||
});
|
||||
} else {
|
||||
peer.on('block', function(block) {
|
||||
peer.emit('merkleblock', block);
|
||||
var index = self.chain.index;
|
||||
backoff = 0;
|
||||
self._response(block);
|
||||
var ret = self.chain.add(block);
|
||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||
self.emit('block', block, peer);
|
||||
if (!ret) {
|
||||
var ip = peer.socket && peer.socket.remoteAddress || '0.0.0.0';
|
||||
self.emit('debug', 'block (%s): %s', ip, utils.revHex(block.hash('hex')));
|
||||
self.emit('debug', 'block (%s): loading locator hashes for getblocks', ip);
|
||||
peer._latestBlock = { hash: block.hash('hex'), ts: block.ts };
|
||||
if (!self._latestBlock || peer._latestBlock.ts > self._latestBlock.ts) {
|
||||
self._latestBlock = peer._latestBlock;
|
||||
}
|
||||
peer.loadBlocks(self.locatorHashes(), 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -350,6 +404,32 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype.locatorHashes = function() {
|
||||
var self = this;
|
||||
var hashes = this.chain.index.hashes;
|
||||
var indicies = [];
|
||||
var top = hashes.length - 1;
|
||||
var step = 1, start = 0;
|
||||
|
||||
for (var i = top; i > 0; i -= step, ++start) {
|
||||
if (start >= 10) step *= 2;
|
||||
indicies.push(hashes[i]);
|
||||
}
|
||||
|
||||
indicies.push(hashes[0]);
|
||||
|
||||
indicies = indicies.reduce(function(out, hash) {
|
||||
if (!~out.indexOf(hash)) {
|
||||
out.push(hash);
|
||||
}
|
||||
return out;
|
||||
}, []);
|
||||
|
||||
this._lastLocator = Date.now();
|
||||
|
||||
return indicies;
|
||||
};
|
||||
|
||||
Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
var i = this.peers.pending.indexOf(peer);
|
||||
if (i !== -1)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user