use getheaders.
This commit is contained in:
parent
3e5995f741
commit
484b1f2817
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user