peer/pool: add origin satoshi protocol.

This commit is contained in:
Christopher Jeffrey 2014-06-09 16:23:49 -05:00
parent a6584ae821
commit feda74523f
3 changed files with 201 additions and 28 deletions

View File

@ -37,6 +37,18 @@ function Chain(options) {
}; };
this.request = new utils.RequestCache(); 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); this.fromJSON(preload);
// Last TS after preload, needed for fill percent // Last TS after preload, needed for fill percent
@ -198,6 +210,31 @@ Chain.prototype.add = function add(block) {
return; 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 res = false;
var initial = block; var initial = block;
do { do {
@ -388,6 +425,20 @@ Chain.prototype.getLast = function getLast(cb) {
return cb(this.index.hashes[this.index.hashes.length - 1]); 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() { Chain.prototype.toJSON = function toJSON() {
var keep = 1000; var keep = 1000;

View File

@ -89,6 +89,14 @@ Peer.prototype._init = function init() {
self._error(err); 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() { this._ping.timer = setInterval(function() {
self._write(self.framer.ping([ self._write(self.framer.ping([
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
@ -98,9 +106,7 @@ Peer.prototype._init = function init() {
// Send hello // Send hello
this._write(this.framer.version({ this._write(this.framer.version({
height: this.options.startHeight != null height: this.pool.chain.getStartHeight(),
? this.options.startHeight
: 0,
relay: this.options.relay relay: this.options.relay
})); }));
@ -167,18 +173,8 @@ Peer.prototype.broadcast = function broadcast(items) {
}; };
Peer.prototype.updateWatch = function updateWatch() { Peer.prototype.updateWatch = function updateWatch() {
if (!this.pool.options.relay) { 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);
});
}
return; return;
}
if (this.ack) if (this.ack)
this._write(this.framer.filterLoad(this.bloom, 'none')); this._write(this.framer.filterLoad(this.bloom, 'none'));
@ -438,11 +434,57 @@ Peer.prototype._handleInv = function handleInv(items) {
this.emit('blocks', blocks); this.emit('blocks', blocks);
if (!this.pool.options.relay) { if (!this.pool.options.relay) {
if (txs.length) var self = this;
if (txs.length) {
this.emit('txs', txs.map(function(tx) { this.emit('txs', txs.map(function(tx) {
return tx.hash; 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; return;
} }

View File

@ -40,7 +40,9 @@ function Pool(options) {
storage: this.storage, storage: this.storage,
// Since regular blocks contain transactions and full merkle // Since regular blocks contain transactions and full merkle
// trees, it's risky to cache 2000 blocks. Let's do 100. // 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.watchMap = {};
this.bloom = new bcoin.bloom(8 * 1024, this.bloom = new bcoin.bloom(8 * 1024,
@ -98,10 +100,41 @@ Pool.prototype._init = function _init() {
var self = this; var self = this;
this.chain.on('missing', function(hash, preload, parent) { this.chain.on('missing', function(hash, preload, parent) {
if (!self.options.relay) return;
self._request('block', hash, { force: true }); self._request('block', hash, { force: true });
self._scheduleRequests(); self._scheduleRequests();
self._loadRange(preload); 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() { Pool.prototype._addLoader = function _addLoader() {
@ -157,6 +190,8 @@ Pool.prototype._addLoader = function _addLoader() {
// Split blocks and request them using multiple peers // Split blocks and request them using multiple peers
peer.on('blocks', function(hashes) { peer.on('blocks', function(hashes) {
if (!self.options.relay) return;
if (hashes.length === 0) { if (hashes.length === 0) {
// Reset global load // Reset global load
self.block.lastHash = null; self.block.lastHash = null;
@ -198,6 +233,8 @@ Pool.prototype.isFull = function isFull() {
}; };
Pool.prototype._loadRange = function _loadRange(hashes, force) { Pool.prototype._loadRange = function _loadRange(hashes, force) {
if (!this.options.relay) return;
if (!hashes) if (!hashes)
return; return;
@ -220,6 +257,8 @@ Pool.prototype._loadRange = function _loadRange(hashes, force) {
}; };
Pool.prototype._load = function _load() { Pool.prototype._load = function _load() {
if (!this.options.relay) return;
if (this.request.queue.length >= this.load.hwm) { if (this.request.queue.length >= this.load.hwm) {
this.load.hiReached = true; this.load.hiReached = true;
return false; return false;
@ -295,19 +334,34 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
self._scheduleRequests(); self._scheduleRequests();
}); });
peer.on('merkleblock', function(block) { if (this.options.relay) {
// Reset backoff, peer seems to be responsive peer.on('merkleblock', function(block) {
backoff = 0; // Reset backoff, peer seems to be responsive
backoff = 0;
self._response(block); self._response(block);
self.chain.add(block); self.chain.add(block);
self.emit('chain-progress', self.chain.fillPercent(), peer); self.emit('chain-progress', self.chain.fillPercent(), peer);
self.emit('block', block, peer); self.emit('block', block, peer);
}); });
} else {
if (!this.options.relay) {
peer.on('block', function(block) { 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) { Pool.prototype._removePeer = function _removePeer(peer) {
var i = this.peers.pending.indexOf(peer); var i = this.peers.pending.indexOf(peer);
if (i !== -1) if (i !== -1)