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();
// 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;

View File

@ -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;
}

View File

@ -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)