use getheaders.

This commit is contained in:
Christopher Jeffrey 2016-01-03 05:08:52 -08:00
parent 3e5995f741
commit 484b1f2817
5 changed files with 323 additions and 326 deletions

View File

@ -42,14 +42,22 @@ function Block(data, subtype) {
this.network = data.network || false;
this.relayedBy = data.relayedBy || '0.0.0.0';
this.valid = null;
this._hash = null;
// List of matched TXs
this.tx = [];
this.invalid = false;
if (this.subtype === 'merkleblock') {
// Verify partial merkle tree and fill `ts` array
this.invalid = !this._verifyMerkle();
} else if (this.subtype === 'block') {
if (!this.subtype) {
if (this.hashes.length)
this.subtype = 'merkleblock';
else if (this.txs.length)
this.subtype = 'block';
else
this.subtype = 'header';
}
if (this.subtype === 'block') {
this.txs = this.txs.map(function(tx) {
tx.network = self.network;
tx.relayedBy = self.relayedBy;
@ -67,11 +75,9 @@ function Block(data, subtype) {
// this.height = height;
// }
// }
this.invalid = !this._checkBlock();
}
this._hash = null;
this.verify();
}
Block.prototype.hash = function hash(enc) {
@ -97,7 +103,14 @@ Block.prototype.abbr = function abbr() {
};
Block.prototype.verify = function verify() {
return !this.invalid && utils.testTarget(this.bits, this.hash());
if (this.valid == null)
this.valid = this._verify();
return this.valid;
};
Block.verify = function verify(data, subtype) {
var block = new Block(data, subtype);
return block.verify();
};
Block.prototype.render = function render() {
@ -112,7 +125,7 @@ Block.prototype.hasTX = function hasTX(hash) {
return this.tx.indexOf(hash) !== -1;
};
Block.prototype._verifyMerkle = function verifyMerkle() {
Block.prototype._verifyPartial = function _verifyPartial() {
var height = 0;
var tx = [];
var i = 0;
@ -121,7 +134,7 @@ Block.prototype._verifyMerkle = function verifyMerkle() {
var flags = this.flags;
var i, root;
if (this.subtype === 'block')
if (this.subtype !== 'merkleblock')
return;
// Count leaves
@ -203,7 +216,7 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() {
// This mimics the behavior of CheckBlockHeader()
// and CheckBlock() in bitcoin/src/main.cpp.
Block.prototype._checkBlock = function checkBlock() {
Block.prototype._verify = function _verify() {
var i, unique, hash, merkleRoot;
// Check proof of work matches claimed amount
@ -214,6 +227,14 @@ Block.prototype._checkBlock = function checkBlock() {
if (this.ts > (Date.now() / 1000) + 2 * 60 * 60)
return false;
if (this.subtype === 'merkleblock') {
if (!this._verifyPartial())
return false;
}
if (this.subtype !== 'block')
return true;
// Size can't be bigger than MAX_BLOCK_SIZE
if (this.txs.length > constants.block.maxSize
|| this.size() > constants.block.maxSize) {
@ -389,9 +410,10 @@ Block.fromJSON = function fromJSON(json) {
parser = new bcoin.protocol.parser();
data = json.subtype === 'merkleblock'
? parser.parseMerkleBlock(raw)
: parser.parseBlock(raw);
if (json.subtype === 'merkleblock')
data = parser.parseMerkleBlock(raw);
else if (json.subtype === 'block' || json.subtype === 'header')
data = parser.parseBlock(raw);
data.network = json.network;
data.relayedBy = json.relayedBy;

View File

@ -48,6 +48,7 @@ function Chain(options) {
hashes: [],
ts: [],
heights: [],
lookup: {},
lastTs: 0
};
@ -56,7 +57,7 @@ function Chain(options) {
this.fromJSON(preload);
// Last TS after preload, needed for fill percent
this.index.lastTs = this.index.ts[0];
this.index.lastTs = this.index.ts[this.index.ts.length - 1];
bcoin.chain.global = this;
@ -177,6 +178,8 @@ Chain.prototype._addIndex = function _addIndex(hash, ts, height) {
this.index.hashes.splice(pos, 0, hash);
this.index.heights.splice(pos, 0, height);
this.index.bloom.add(hash, 'hex');
// this.index.lookup[hash] = pos;
// this.index.lookup[height] = pos;
this._save(hash, {
ts: ts,
@ -196,6 +199,7 @@ Chain.prototype.resetLastCheckpoint = function resetLastCheckpoint(height) {
Chain.prototype.resetHeight = function resetHeight(height) {
var self = this;
var index = this.index.heights.indexOf(height);
var ahead = this.index.hashes.slice(index + 1);
if (index < 0)
throw new Error('Cannot reset to height of ' + height);
@ -212,21 +216,22 @@ Chain.prototype.resetHeight = function resetHeight(height) {
this.index.hashes.forEach(function(hash) {
self.index.bloom.add(hash, 'hex');
});
};
Chain.prototype._heightByTime = function _heightByTime(ts) {
for (var i = this.index.ts.length - 1; i >= 0; i--) {
if (ts >= this.index.ts[i])
return this.index.heights[i];
}
return -1;
this.index.lastTs = Math.min(
this.index.lastTs,
this.index.ts[this.index.ts.length - 1]
);
ahead.forEach(function(hash) {
self._delete(hash);
});
};
Chain.prototype.resetTime = function resetTime(ts) {
var height = this._heightByTime(ts);
if (height === -1)
var entry = this.byTime(ts);
if (!entry)
return;
return this.resetHeight(height);
return this.resetHeight(entry.height);
};
Chain.prototype._killFork = function _killFork(probe) {
@ -407,20 +412,6 @@ Chain.prototype.has = function has(hash, noIndex, cb) {
return cb(false);
};
Chain.prototype.byHeight = function byHeight(height) {
var index = this.index.heights.indexOf(height);
if (index === -1)
return null;
return {
index: index,
hash: this.index.hashes[index],
ts: this.index.ts[index],
height: this.index.heights[index]
};
};
Chain.prototype.byHash = function byHash(hash) {
if (Array.isArray(hash))
hash = utils.toHex(hash);
@ -440,6 +431,28 @@ Chain.prototype.byHash = function byHash(hash) {
};
};
Chain.prototype.byHeight = function byHeight(height) {
var index = this.index.heights.indexOf(height);
if (index === -1)
return null;
return {
index: index,
hash: this.index.hashes[index],
ts: this.index.ts[index],
height: this.index.heights[index]
};
};
Chain.prototype.byTime = function byTime(ts) {
for (var i = this.index.ts.length - 1; i >= 0; i--) {
if (ts >= this.index.ts[i])
return this.byHeight(this.index.heights[i]);
}
return null;
};
Chain.prototype.hasBlock = function hasBlock(hash) {
if (Array.isArray(hash))
hash = utils.toHex(hash);
@ -508,33 +521,6 @@ Chain.prototype.getTip = function() {
};
};
Chain.prototype.get = function get(hash, force, cb) {
var i, block;
if (typeof force === 'function') {
cb = force;
force = false;
}
// Cached block found
if (!force) {
if (this.block.bloom.test(hash, 'hex')) {
block = this.getCache(hash);
if (block) {
bcoin.utils.nextTick(cb.bind(null, block));
return block;
}
// False positive:
// assert(false);
}
if (this.hasOrphan(hash))
return cb(this.getOrphan(hash));
}
if (this.request.add(hash, cb))
this.emit('missing', hash, null, null);
};
Chain.prototype.isFull = function isFull() {
// < 40m since last block
if (this.request.count)
@ -546,11 +532,25 @@ Chain.prototype.isFull = function isFull() {
};
Chain.prototype.fillPercent = function fillPercent() {
var total = (+new Date() / 1000 - 40 * 60) - this.index.lastTs;
var current = this.index.ts[this.index.ts.length - 1] - this.index.lastTs;
var total = (+new Date() / 1000 - 40 * 60) - this.index.ts[0];
var current = this.index.ts[this.index.ts.length - 1] - this.index.ts[0];
return Math.max(0, Math.min(current / total, 1));
};
Chain.prototype.hashRange = function hashRange(start, end) {
var hashes;
start = this.chain.byTime(start);
end = this.chain.byTime(end);
if (!start || !end)
return [];
hashes = this.chain.index.hashes.slice(start.index, end.index + 1);
return hashes;
};
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
var ts, hashes, heights, zip, i, count;
@ -591,31 +591,22 @@ Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
return cb(hashes, count);
};
Chain.prototype.getLast = function getLast(cb) {
if (this.loading) {
this.once('load', function() {
this.getLast(cb);
});
return;
}
cb = utils.asyncify(cb);
return cb(this.index.hashes[this.index.hashes.length - 1]);
};
Chain.prototype.getStartHeight = function getStartHeight() {
return 0;
};
Chain.prototype.locatorHashes = function locatorHashes(start) {
assert(start == null);
if (start != null) {
if (typeof start === 'number')
start = this.byHeight(start);
else
start = this.byHash(start) || start;
if (this.index.hashes.length === 1)
return [this.index.hashes[0]];
if (start && start.index != null)
start = start.index;
}
return [
this.index.hashes[this.index.hashes.length - 1],
this.index.hashes[0]
];
return bcoin.fullChain.prototype.locatorHashes.call(this, start);
};
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {

View File

@ -64,7 +64,7 @@ function Chain(options) {
});
// Last TS after preload, needed for fill percent
this.index.lastTs = this.index.entries[0].ts;
this.index.lastTs = this.index.entries[this.index.entries.length - 1].ts;
bcoin.chain.global = this;
@ -145,7 +145,10 @@ Chain.prototype.resetLastCheckpoint = function resetLastCheckpoint(height) {
Chain.prototype.resetHeight = function resetHeight(height) {
var self = this;
var forked = this.index.entries.slice(height + 1);
var ahead = this.index.entries.slice(height + 1);
if (height >= this.index.entries - 1)
throw new Error('Cannot reset to height of ' + height);
this.orphan.map = {};
this.orphan.bmap = {};
@ -157,26 +160,21 @@ Chain.prototype.resetHeight = function resetHeight(height) {
}, {});
this.index.hashes.length = height + 1;
this.index.lastTs = this.index.entries[0].ts;
this.index.lastTs = Math.min(
this.index.lastTs,
this.index.entries[this.index.entries.length - 1].ts
);
forked.forEach(function(entry) {
self._delete(entry);
ahead.forEach(function(entry) {
self._delete(entry.hash);
});
};
Chain.prototype._heightByTime = function _heightByTime(ts) {
for (var i = this.index.entries.length - 1; i >= 0; i--) {
if (ts >= this.index.entries[i].ts)
return this.index.entries[i].height;
}
return -1;
};
Chain.prototype.resetTime = function resetTime(ts) {
var height = this._heightByTime(ts);
if (height === -1)
var entry = this.byTime(ts);
if (!entry)
return;
return this.resetHeight(height);
return this.resetHeight(entry.height);
};
Chain.prototype.add = function add(block) {
@ -301,6 +299,14 @@ Chain.prototype.byHash = function byHash(hash) {
return this.byHeight(this.index.heights[hash]);
};
Chain.prototype.byTime = function byTime(ts) {
for (var i = this.index.entries.length - 1; i >= 0; i--) {
if (ts >= this.index.entries[i].ts)
return this.index.entries[i];
}
return null;
};
Chain.prototype.hasBlock = function hasBlock(hash) {
return !!this.byHash(hash);
};
@ -336,10 +342,6 @@ Chain.prototype.getTip = function getTip() {
return this.index.entries[this.index.entries.length - 1];
};
Chain.prototype.get = function get(hash, force, cb) {
assert(false);
};
Chain.prototype.isFull = function isFull() {
var last = this.index.entries[this.index.entries.length - 1].ts;
var delta = (+new Date() / 1000) - last;
@ -352,19 +354,22 @@ Chain.prototype.fillPercent = function fillPercent() {
return Math.max(0, Math.min(current / total, 1));
};
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
assert(false);
Chain.prototype.hashRange = function hashRange(start, end) {
var hashes;
start = this.chain.byTime(start);
end = this.chain.byTime(end);
if (!start || !end)
return [];
hashes = this.chain.index.hashes.slice(start.height, end.height + 1);
return hashes;
};
Chain.prototype.getLast = function getLast(cb) {
if (this.loading) {
this.once('load', function() {
this.getLast(cb);
});
return;
}
cb = utils.asyncify(cb);
return cb(this.index.hashes[this.index.hashes.length - 1]);
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
assert(false);
};
Chain.prototype.getStartHeight = function getStartHeight() {
@ -387,8 +392,10 @@ Chain.prototype.locatorHashes = function locatorHashes(start) {
if (typeof start === 'string') {
// Hash
if (this.index.heights[start])
if (this.index.heights[start] != null)
top = this.index.heights[start];
else
hashes.push(start);
} else if (typeof start === 'number') {
// Height
top = start;
@ -396,7 +403,8 @@ Chain.prototype.locatorHashes = function locatorHashes(start) {
i = top;
for (;;) {
hashes.push(chain[i]);
if (chain[i])
hashes.push(chain[i]);
i = i - step;
if (i <= 0) {
hashes.push(chain[0]);

View File

@ -502,7 +502,7 @@ Peer.prototype._handleInv = function handleInv(items) {
});
if (blocks.length === 1)
this.bestBlock = utils.toHex(blocks[0]);
this.bestHash = utils.toHex(blocks[0]);
this.emit('blocks', blocks);

View File

@ -94,9 +94,9 @@ function Pool(options) {
};
this.block = {
lastHash: null,
bestHeight: 0,
bestHash: null
bestHash: null,
type: this.options.fullNode ? 'block' : 'filtered'
};
this.request = {
@ -154,13 +154,11 @@ Pool.prototype._init = function _init() {
for (i = 0; i < this.size; i++)
this._addPeer(0);
this._load();
this.chain.on('missing', function(hash, preload, parent) {
if (!self.options.fullNode) {
self._request('filtered', hash, { force: true });
self._request(self.block.type, hash, { force: true });
self._scheduleRequests();
self._loadRange(preload);
// self._loadRange(preload);
}
});
@ -226,7 +224,7 @@ Pool.prototype._addLoader = function _addLoader() {
});
function load() {
self._load();
// self._load();
}
interval = setInterval(load, this.load.interval);
@ -236,7 +234,6 @@ Pool.prototype._addLoader = function _addLoader() {
if (self.chain.isFull()) {
clearTimeout(timer);
self.emit('full');
self.block.lastHash = null;
return;
}
@ -245,89 +242,68 @@ Pool.prototype._addLoader = function _addLoader() {
timer = setTimeout(destroy, this.load.timeout);
peer.on('merkleblock', function(block) {
self._handleBlock(block, peer);
});
peer.on('block', function(block) {
self._handleBlock(block, peer);
});
// Split blocks and request them using multiple peers
peer.on('blocks', function(hashes) {
var i, hash;
self._handleInv(hashes, peer);
});
self.emit('blocks', hashes);
// Split blocks and request them using multiple peers
peer.on('headers', function(headers) {
var i, header, last, block;
if (hashes.length === 0) {
// Reset global load
self.block.lastHash = null;
if (headers.length === 0)
return;
}
if (!self.options.fullNode) {
// Request each block
for (i = 0; i < hashes.length; i++) {
self._request('filtered', hashes[i]);
}
// Push our getblocks packet
self._scheduleRequests();
// The part of the response is in chain, no need to escalate requests
async.every(hashes, function(hash, cb) {
self.chain.has(utils.toHex(hash), function(res) {
cb(!res);
});
}, function(allNew) {
if (!allNew) {
self.block.lastHash = null;
return;
}
// Store last hash to continue global load
self.block.lastHash = hashes[hashes.length - 1];
clearTimeout(timer);
// Reinstantiate timeout
if (self._load())
timer = setTimeout(destroy, self.load.timeout);
});
return;
}
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
// Resolve orphan chain
if (self.chain.hasOrphan(hash)) {
peer.loadBlocks(
self.chain.locatorHashes(),
self.chain.getOrphanRoot(hash)
);
continue;
}
// Restart the entire getblocks process
if (i === hashes.length - 1)
peer.loadBlocks(self.chain.locatorHashes(), 0);
// Request block if we don't have it
if (!self.chain.hasBlock(hash) && !self.chain.hasOrphan(hash))
self._request('block', hash);
}
self.emit('debug',
'Requesting %s block packets from %s with getdata',
self.request.active,
peer.address
);
'Recieved %s headers from %s',
headers.length,
peer.address);
// Push our getblocks packet
self.emit('headers', headers);
for (i = 0; i < headers.length; i++) {
block = bcoin.block(headers[i], 'header');
if (last && block.prevBlock !== last.hash('hex'))
break;
if (!block.verify())
break;
if (!self.chain.hasBlock(block) && !self.chain.hasOrphan(block))
self._request(self.block.type, block.hash('hex'));
// We could do headers-first:
// self._addIndex(block, peer);
// Resolve orphan chain
// if (self.chain.hasOrphan(block)) {
// peer.loadHeaders(
// self.chain.locatorHashes(),
// self.chain.getOrphanRoot(block)
// );
// continue;
// }
last = block;
}
// Restart the getheaders process
if (last && headers.length >= 1999)
peer.loadHeaders(self.chain.locatorHashes(last), 0);
// peer.loadHeaders([last.hash('hex')], 0);
// Schedule our requests
self._scheduleRequests();
// Store last hash because we can
self.block.lastHash = hashes[hashes.length - 1];
// Reset interval to avoid calling getblocks unnecessarily
// Reset interval to avoid calling getheaders unnecessarily
clearInterval(interval);
interval = setInterval(load, self.load.interval);
@ -337,34 +313,44 @@ Pool.prototype._addLoader = function _addLoader() {
});
};
Pool.prototype._handleMerkle = function _handleMerkle(block, peer) {
if (this.options.fullNode)
return;
Pool.prototype._handleInv = function _handleInv(hashes, peer) {
var i, hash;
this._response(block);
this.chain.add(block);
this.emit('chain-progress', this.chain.fillPercent(), peer);
this.emit('block', block, peer);
for (i = 0; i < hashes.length; i++) {
hash = utils.toHex(hashes[i]);
if (!this.chain.hasBlock(hash) && !this.chain.hasOrphan(hash))
this.peers.load.loadHeaders(this.chain.locatorHashes(), hash);
}
};
Pool.prototype._handleBlock = function _handleBlock(block, peer) {
var self = this;
var hash, size, orphan, err;
if (!this.options.fullNode)
var requested = this._response(block);
// Someone is sending us blocks without us requesting them.
if (!requested)
return;
// Emulate bip37 - emit all the "watched" txs
if (this.listeners('watched').length > 0) {
utils.nextTick(function() {
block.txs.forEach(function(tx) {
if (self.isWatched(tx))
self.emit('watched', tx, peer);
});
if (this.options.fullNode
&& this.listeners('watched').length > 0
&& block.verify()) {
block.txs.forEach(function(tx) {
if (self.isWatched(tx))
self.emit('watched', tx, peer);
});
}
this._response(block);
if (!this._addIndex(block, peer))
return;
this.emit('block', block, peer);
};
Pool.prototype._addIndex = function _addIndex(block, peer) {
var self = this;
var hash, size, orphan, err;
hash = block.hash('hex');
size = this.chain.size();
@ -374,26 +360,25 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
if (err)
this.emit('chain-error', err, peer);
this.emit('_block', block, peer);
if (this.chain.hasOrphan(block)) {
// Resolve orphan chain
peer.loadBlocks(
this.chain.locatorHashes(),
this.chain.getOrphanRoot(block)
);
// peer.loadHeaders(
// this.chain.locatorHashes(),
// this.chain.getOrphanRoot(block)
// );
// Emit our orphan if it is new
if (!orphan)
this.emit('orphan', block, peer);
return;
return false;
}
// Do not emit if nothing was added to the chain
if (this.chain.size() === size)
return;
return false;
this.emit('chain-progress', this.chain.fillPercent(), peer);
this.emit('block', block, peer);
return true;
};
Pool.prototype.isFull = function isFull() {
@ -442,31 +427,16 @@ Pool.prototype._load = function _load() {
if (this.peers.load) {
this.emit('debug',
'Requesting inv packet from %s with getblocks',
'Requesting headers packet from %s with getheaders',
this.peers.load.address);
}
if (!this.options.fullNode) {
next = function(hash) {
if (!self.peers.load)
self._addLoader();
else
self.peers.load.loadBlocks([hash], 0);
};
// Load more blocks, starting from last hash
if (this.block.lastHash)
next(this.block.lastHash);
else
this.chain.getLast(next);
if (!this.peers.load) {
this._addLoader();
return true;
}
if (!this.peers.load)
this._addLoader();
else
this.peers.load.loadBlocks(this.chain.locatorHashes(), 0);
this.peers.load.loadHeaders(this.chain.locatorHashes(), 0);
return true;
};
@ -531,7 +501,7 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
peer.on('merkleblock', function(block) {
// Reset backoff, peer seems to be responsive
backoff = 0;
self._handleMerkle(block, peer);
self._handleBlock(block, peer);
});
peer.on('block', function(block) {
@ -565,10 +535,8 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
self.emit('addr', addr, peer);
});
peer.on('blocks', function(blocks) {
if (blocks.length === 1)
self.block.bestHash = peer.bestBlock;
self.emit('blocks', blocks, peer);
peer.on('blocks', function(hashes) {
self._handleInv(hashes, peer);
});
peer.on('txs', function(txs) {
@ -834,6 +802,7 @@ Pool.prototype.searchWallet = function(w) {
Pool.prototype.search = function search(id, range, e) {
var self = this;
var hashes, pending, listener, timeout, done, total;
if (this.options.fullNode)
return;
@ -862,72 +831,70 @@ Pool.prototype.search = function search(id, range, e) {
// Last 5 days by default, this covers 1000 blocks that we have in the
// chain by default
if (!range.end)
range.end = +new Date() / 1000;
range.end = +new Date() / 1000 | 0;
if (!range.start)
range.start = +new Date() / 1000 - 432000;
this.chain.hashesInRange(range.start, range.end, function(hashes, count) {
var waiting = count;
self.emit('debug',
'Search for %s (%s) hashes between %s and %s',
hashes.length,
count,
new Date(range.start * 1000).toISOString(),
new Date(range.end * 1000).toISOString()
);
range.start = (+new Date() / 1000 | 0) - 432000;
if (range.start < this.chain.lastTs) {
if (id)
self.watch(id);
this.watch(id);
self._loadRange(hashes, true);
done = function(res) {
e.emit('end', res);
clearInterval(timeout);
self.removeListener('block', listener);
if (id)
self.unwatch(id);
};
hashes = hashes.slice().reverse();
hashes.forEach(function(hash, i) {
// Get the block that is in index
self.chain.request.add(hash, function(block) {
loadBlock(block, hashes[i + 1]);
});
this.on('block', listener = function(block) {
if (block.ts >= range.end)
done();
});
function loadBlock(block, stop) {
// Stop block reached
if (block.hash('hex') === stop)
return;
total = (range.end - range.start) / network.powTargetSpacing | 0;
if (total === 0)
total = 1;
// Get block's prev and request it and all of it's parents up to
// the next known block hash
self.chain.request.add(block.prevBlock, function(prev) {
done();
timeout = setTimeout(done.bind(null, true), total * 10 * 1000);
// First hash loaded
if (!stop)
return;
this.chain.resetTime(range.start);
// Continue loading blocks
loadBlock(prev, stop);
});
if (this.peers.load)
this.peers.load.destroy();
this._load();
return e;
}
hashes = this.chain.hashRange(range.start, range.end);
pending = hashes.length;
if (id)
this.watch(id);
done = function() {
pending--;
assert(pending >= 0);
e.emit('progress', count - pending, count);
if (pending === 0) {
if (id)
self.unwatch(id);
e.emit('end');
}
};
function done() {
waiting--;
assert(waiting >= 0);
e.emit('progress', count - waiting, count);
if (waiting === 0) {
if (id)
self.unwatch(id);
e.emit('end');
}
}
// Empty search
if (hashes.length === 0) {
bcoin.utils.nextTick(function() {
e.emit('end', true);
});
}
hashes.forEach(function(hash) {
self._request('filtered', hash, { force: true }, done);
});
if (hashes.length === 0) {
bcoin.utils.nextTick(function() {
e.emit('end', true);
});
}
return e;
};
@ -958,8 +925,8 @@ Pool.prototype._request = function _request(type, hash, options, cb) {
}
// Block should be not in chain, or be requested
if (!options.force && (type === 'block' || type === 'filtered'))
return this.chain.has(hash, true, next);
// if (!options.force && (type === 'block' || type === 'filtered'))
// return this.chain.has(hash, true, next);
return next(false);
};
@ -1021,38 +988,36 @@ Pool.prototype._doRequests = function _doRequests() {
if (above && below && this.load.hiReached)
this._load();
if (!this.options.fullNode) {
mapReq = function(item) {
return item.start(this.peers.block[i]);
};
if (this.options.fullNode) {
req = items.map(function(item) {
return item.start(this.peers.load);
}, this);
// Split list between peers
red = this.redundancy;
count = this.peers.block.length;
split = Math.ceil(items.length * red / count);
for (i = 0, off = 0; i < count; i += red, off += split) {
req = items.slice(off, off + split).map(mapReq, this);
for (j = 0; j < red && i + j < count; j++)
this.peers.block[i + j].getData(req);
}
this.peers.load.getData(req);
return;
}
req = items.map(function(item) {
return item.start(this.peers.load);
}, this);
mapReq = function(item) {
return item.start(this.peers.block[i]);
};
this.peers.load.getData(req);
// Split list between peers
red = this.redundancy;
count = this.peers.block.length;
split = Math.ceil(items.length * red / count);
for (i = 0, off = 0; i < count; i += red, off += split) {
req = items.slice(off, off + split).map(mapReq, this);
for (j = 0; j < red && i + j < count; j++)
this.peers.block[i + j].getData(req);
}
};
Pool.prototype.getBlock = function getBlock(hash, cb) {
var type = this.options.fullNode ? 'block' : 'filtered';
if (this.chain.request.add(hash, cb)) {
this._request(type, hash, { force: true });
this._scheduleRequests();
}
this._request('block', hash, { force: true }, function(block) {
cb(null, block);
});
this._scheduleRequests();
};
Pool.prototype.sendBlock = function sendBlock(block) {
@ -1188,15 +1153,26 @@ Pool.prototype.toJSON = function toJSON() {
return {
v: 1,
type: 'pool',
chain: this.chain.toJSON()
chain: this.chain.toJSON(),
requests: this.request.queue.map(function(item) {
return {
type: item.type,
hash: item.hash
};
})
};
};
Pool.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.v, 1);
assert.equal(json.type, 'pool');
this.chain.fromJSON(json.chain);
json.requests.forEach(function(item) {
this._request(item.type, item.hash);
}, this);
return this;
};