add fullnode chain. improve pool.
This commit is contained in:
parent
65dd1727be
commit
146e50dc68
@ -21,6 +21,8 @@ bcoin.tx = require('./bcoin/tx');
|
||||
bcoin.txPool = require('./bcoin/tx-pool');
|
||||
bcoin.block = require('./bcoin/block');
|
||||
bcoin.chain = require('./bcoin/chain');
|
||||
bcoin.spvChain = require('./bcoin/chain');
|
||||
bcoin.fullChain = require('./bcoin/fullchain');
|
||||
bcoin.wallet = require('./bcoin/wallet');
|
||||
bcoin.peer = require('./bcoin/peer');
|
||||
bcoin.pool = require('./bcoin/pool');
|
||||
|
||||
@ -267,7 +267,7 @@ Block.prototype.getNextBlock = function getNextBlock(chain) {
|
||||
chain = chain || bcoin.chain.global;
|
||||
|
||||
if (!chain)
|
||||
return utils.toHex(constants.protocol.zeroHash);
|
||||
return utils.toHex(constants.zeroHash);
|
||||
|
||||
next = chain.getNextBlock(this.hash('hex'));
|
||||
|
||||
|
||||
@ -53,24 +53,12 @@ function Chain(options) {
|
||||
|
||||
this.request = new utils.RequestCache();
|
||||
|
||||
// Start from the genesis block
|
||||
// if we're a full node.
|
||||
if (this.options.fullNode) {
|
||||
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
|
||||
this.index.lastTs = this.index.ts[this.index.ts.length - 1];
|
||||
|
||||
Chain.global = this;
|
||||
bcoin.chain.global = this;
|
||||
|
||||
this.loading = false;
|
||||
this._init();
|
||||
@ -338,7 +326,7 @@ Chain.prototype.add = function add(block) {
|
||||
// At least one block was added
|
||||
res = true;
|
||||
this.block.list.push(block);
|
||||
this._bloomBlock(block);
|
||||
this.block.bloom.add(hash, 'hex');
|
||||
|
||||
// Fullfill request
|
||||
this.request.fullfill(hash, block);
|
||||
@ -371,11 +359,7 @@ Chain.prototype._compress = function compress() {
|
||||
this.block.bloom.reset();
|
||||
|
||||
for (i = 0; i < this.block.list.length; i++)
|
||||
this._bloomBlock(this.block.list[i]);
|
||||
};
|
||||
|
||||
Chain.prototype._bloomBlock = function _bloomBlock(block) {
|
||||
this.block.bloom.add(block.hash(), 'hex');
|
||||
this.block.bloom.add(this.block.list[i].hash('hex'), 'hex');
|
||||
};
|
||||
|
||||
Chain.prototype.has = function has(hash, noProbe, cb) {
|
||||
@ -413,6 +397,45 @@ Chain.prototype.has = function has(hash, noProbe, cb) {
|
||||
return cb(!!this.orphan.map[hash]);
|
||||
};
|
||||
|
||||
Chain.prototype.byHeight = function byHeight(height) {
|
||||
if (this.loading)
|
||||
return null;
|
||||
|
||||
var index = this.index.heights.indexOf(height);
|
||||
|
||||
if (index === -1)
|
||||
return -1;
|
||||
|
||||
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 (this.loading)
|
||||
return null;
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
var index = this.index.hashes.indexOf(hash);
|
||||
|
||||
if (index === -1)
|
||||
return -1;
|
||||
|
||||
return {
|
||||
index: index,
|
||||
hash: this.index.hashes[index],
|
||||
ts: this.index.ts[index],
|
||||
height: this.index.heights[index]
|
||||
};
|
||||
};
|
||||
|
||||
Chain.prototype.hasBlock = function hasBlock(hash) {
|
||||
if (this.loading)
|
||||
return false;
|
||||
@ -533,97 +556,49 @@ Chain.prototype.getLast = function getLast(cb) {
|
||||
};
|
||||
|
||||
Chain.prototype.getStartHeight = function getStartHeight() {
|
||||
if (!this.options.fullNode) {
|
||||
if (this.options.startHeight != null) {
|
||||
return this.options.startHeight;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return this.index.heights[this.index.heights.length - 1];
|
||||
if (this.options.startHeight != null)
|
||||
return this.options.startHeight;
|
||||
return 0;
|
||||
};
|
||||
|
||||
Chain.prototype.locatorHashes = function locatorHashes(start) {
|
||||
var chain = this.index.hashes;
|
||||
var hashes = [];
|
||||
var top = chain.length - 1;
|
||||
var step = 1;
|
||||
var i;
|
||||
assert(start == null);
|
||||
|
||||
if (typeof start === 'string') {
|
||||
// Hash
|
||||
for (i = top; i >= 0; i--) {
|
||||
if (chain[i] === start) {
|
||||
top = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (typeof start === 'number') {
|
||||
// Height
|
||||
start = this.index.heights.indexOf(start);
|
||||
if (start !== -1)
|
||||
top = start;
|
||||
}
|
||||
if (this.index.hashes.length === 1)
|
||||
return [this.index.hashes[0]];
|
||||
|
||||
i = top;
|
||||
for (;;) {
|
||||
hashes.push(chain[i]);
|
||||
i = i - step;
|
||||
if (i <= 0) {
|
||||
hashes.push(chain[0]);
|
||||
break;
|
||||
}
|
||||
if (hashes.length >= 10)
|
||||
step *= 2;
|
||||
}
|
||||
|
||||
return hashes;
|
||||
return [
|
||||
this.index.hashes[this.index.hashes.length - 1],
|
||||
this.index.hashes[0]
|
||||
];
|
||||
};
|
||||
|
||||
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
|
||||
var self = this;
|
||||
var root = hash;
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
while (this.orphan.bmap[hash]) {
|
||||
root = hash;
|
||||
hash = this.orphan.bmap[hash].prevBlock;
|
||||
}
|
||||
|
||||
return root;
|
||||
assert(false);
|
||||
};
|
||||
|
||||
Chain.prototype.getHeight = function getHeight(hash) {
|
||||
var i, height;
|
||||
var entry = this.byHash(hash);
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
i = this.index.hashes.indexOf(hash);
|
||||
height = this.index.heights[i];
|
||||
|
||||
if (height == null)
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
return height;
|
||||
return entry.height;
|
||||
};
|
||||
|
||||
Chain.prototype.getNextBlock = function getNextBlock(hash) {
|
||||
var height, nextHeight, i;
|
||||
var entry = this.byHeight(hash);
|
||||
var nextHeight;
|
||||
|
||||
height = this.getHeight(hash);
|
||||
nextHeight = this.index.heights[i + 1];
|
||||
i = this.index.heights.indexOf(nextHeight);
|
||||
|
||||
if (nextHeight !== height + 1)
|
||||
if (!entry)
|
||||
return null;
|
||||
|
||||
return this.index.hashes[i] || null;
|
||||
nextHeight = this.index.heights[entry.index + 1];
|
||||
|
||||
if (nextHeight == null || nextHeight !== entry.height + 1)
|
||||
return null;
|
||||
|
||||
return this.index.hashes[entry.index + 1] || null;
|
||||
};
|
||||
|
||||
Chain.prototype.toJSON = function toJSON() {
|
||||
@ -708,4 +683,8 @@ Chain.prototype.fromJSON = function fromJSON(json) {
|
||||
this.index.bloom.add(this.index.hashes[i], 'hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Chain;
|
||||
|
||||
557
lib/bcoin/fullchain.js
Normal file
557
lib/bcoin/fullchain.js
Normal file
@ -0,0 +1,557 @@
|
||||
/**
|
||||
* fullchain.js - fullnode blockchain management for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* https://github.com/indutny/bcoin
|
||||
*/
|
||||
|
||||
var inherits = require('inherits');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var bcoin = require('../bcoin');
|
||||
var bn = require('bn.js');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
|
||||
/**
|
||||
* Chain
|
||||
*/
|
||||
|
||||
function Chain(options) {
|
||||
if (!(this instanceof Chain))
|
||||
return new Chain(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.options = options || {};
|
||||
this.prefix = 'bt/chain/';
|
||||
this.storage = this.options.storage;
|
||||
this.strict = this.options.strict || false;
|
||||
this.cacheLimit = this.options.cacheLimit || 2000;
|
||||
|
||||
this.tip = null;
|
||||
|
||||
this.orphan = {
|
||||
map: {},
|
||||
bmap: {},
|
||||
count: 0
|
||||
};
|
||||
|
||||
this.index = {
|
||||
entries: [],
|
||||
// Get hash by height
|
||||
hashes: [],
|
||||
// Get height by hash
|
||||
heights: {},
|
||||
lastTs: 0
|
||||
};
|
||||
|
||||
this.fromJSON({
|
||||
v: 1,
|
||||
type: 'chain',
|
||||
network: network.type,
|
||||
entries: [
|
||||
{
|
||||
hash: utils.toHex(network.genesis._hash),
|
||||
version: network.genesis.version,
|
||||
// prevBlock: utils.toHex(network.genesis.prevBlock),
|
||||
ts: network.genesis.ts,
|
||||
bits: network.genesis.bits,
|
||||
height: 0
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Last TS after preload, needed for fill percent
|
||||
this.index.lastTs = this.index.entries[this.index.entries.length - 1].ts;
|
||||
|
||||
bcoin.chain.global = this;
|
||||
|
||||
this.loading = false;
|
||||
this._init();
|
||||
}
|
||||
|
||||
inherits(Chain, EventEmitter);
|
||||
|
||||
Chain.prototype._init = function _init() {
|
||||
var self = this;
|
||||
var s;
|
||||
|
||||
if (!this.storage)
|
||||
return;
|
||||
|
||||
utils.nextTick(function() {
|
||||
self.emit('debug', 'Chain is loading.');
|
||||
});
|
||||
|
||||
this.loading = true;
|
||||
|
||||
s = this.storage.createReadStream({
|
||||
start: this.prefix,
|
||||
end: this.prefix + 'z'
|
||||
});
|
||||
|
||||
s.on('data', function(data) {
|
||||
data.value.hash = data.key.slice(self.prefix.length);
|
||||
self._addIndex(ChainBlock.fromJSON(self, data.value));
|
||||
});
|
||||
|
||||
s.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
s.on('end', function() {
|
||||
self.loading = false;
|
||||
self.emit('load');
|
||||
self.emit('debug', 'Chain successfully loaded.');
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype._addIndex = function _addIndex(entry) {
|
||||
var self = this;
|
||||
|
||||
if (this.index.entries[entry.hash])
|
||||
return new Error('Already added.');
|
||||
|
||||
if (this.index.hashes[entry.height] === entry.hash)
|
||||
return new Error('Duplicate height.');
|
||||
|
||||
checkpoint = network.checkpoints[entry.height];
|
||||
if (checkpoint) {
|
||||
this.emit('checkpoint', entry.height, entry.hash, checkpoint);
|
||||
if (hash !== checkpoint) {
|
||||
this.resetLastCheckpoint(entry.height);
|
||||
this.emit('fork', entry.height, entry.hash, checkpoint);
|
||||
return new Error('Forked chain at checkpoint.');
|
||||
}
|
||||
}
|
||||
|
||||
this.index.entries[entry.height] = entry;
|
||||
this.index.hashes[entry.height] = entry.hash;
|
||||
this.index.heights[entry.hash] = entry.height;
|
||||
|
||||
this._save(entry);
|
||||
};
|
||||
|
||||
Chain.prototype.resetLastCheckpoint = function resetLastCheckpoint(height) {
|
||||
var lastHeight = Object.keys(network.checkpoints).sort().indexOf(height) - 1;
|
||||
|
||||
if (lastHeight < 0)
|
||||
i = 0;
|
||||
|
||||
this.resetHeight(lastHeight);
|
||||
};
|
||||
|
||||
Chain.prototype.resetHeight = function resetHeight(height) {
|
||||
var self = this;
|
||||
var forked = this.index.entries.slice(height + 1);
|
||||
|
||||
this.orphan.map = {};
|
||||
this.orphan.bmap = {};
|
||||
this.orphan.count = 0;
|
||||
this.index.entries.length = height + 1;
|
||||
this.index.heights = this.index.entries.reduce(function(out, entry) {
|
||||
out[entry.hash] = entry.height;
|
||||
return out;
|
||||
}, {});
|
||||
this.index.hashes.length = height + 1;
|
||||
|
||||
this.index.lastTs = this.index.entries[this.index.entries.length - 1].ts;
|
||||
|
||||
if (!this.storage)
|
||||
return;
|
||||
|
||||
forked.forEach(function(entry) {
|
||||
self._del(entry);
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype.add = function add(block) {
|
||||
if (this.loading) {
|
||||
this.once('load', function() {
|
||||
this.add(block);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var res = false;
|
||||
var err = null;
|
||||
var initial = block;
|
||||
var hash, prev, prevProbe, range, i, entry;
|
||||
|
||||
for (;;) {
|
||||
// No need to revalidate orphans
|
||||
if (!res && !block.verify()) {
|
||||
err = new Error('Block verification failed.');
|
||||
break;
|
||||
}
|
||||
|
||||
hash = block.hash('hex');
|
||||
prev = block.prevBlock;
|
||||
|
||||
// If the block is already known to be an orphan
|
||||
if (this.orphan.map[prev]) {
|
||||
err = new Error('Block is a known orphan.');
|
||||
break;
|
||||
}
|
||||
|
||||
i = this.index.heights[prev];
|
||||
|
||||
// If previous block wasn't ever seen - add current to orphans
|
||||
if (i == null) {
|
||||
this.orphan.count++;
|
||||
this.orphan.map[prev] = block;
|
||||
this.orphan.bmap[hash] = block;
|
||||
break;
|
||||
}
|
||||
|
||||
entry = new ChainBlock(this, {
|
||||
hash: hash,
|
||||
version: block.version,
|
||||
// prevBlock: prev,
|
||||
ts: block.ts,
|
||||
bits: block.bits,
|
||||
height: i + 1
|
||||
});
|
||||
|
||||
// Already have block
|
||||
if (this.index.hashes[entry.height] === hash)
|
||||
break;
|
||||
|
||||
assert(this.index.heights[entry.hash] == null);
|
||||
|
||||
// Remove forked nodes from storage, if shorter chain is detected
|
||||
if (this.index.hashes[entry.height]) {
|
||||
if (!this.tip) {
|
||||
this.tip = entry;
|
||||
} else if (this.tip.chainwork.cmp(entry.chainwork) < 0) {
|
||||
// Found fork
|
||||
this.resetHeight(entry.height - 1);
|
||||
this.tip = entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Validated known block at this point - add it to index
|
||||
this._addIndex(entry);
|
||||
|
||||
// At least one block was added
|
||||
res = true;
|
||||
|
||||
if (!this.orphan.map[hash])
|
||||
break;
|
||||
|
||||
// We have orphan child for this block - add it to chain
|
||||
block = this.orphan.map[hash];
|
||||
delete this.orphan.bmap[block.hash('hex')];
|
||||
delete this.orphan.map[hash];
|
||||
this.orphan.count--;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
Chain.prototype.has = function has(hash, noProbe, cb) {
|
||||
var res;
|
||||
|
||||
if (typeof noProbe === 'function') {
|
||||
cb = noProbe;
|
||||
noProbe = false;
|
||||
}
|
||||
|
||||
if (this.loading) {
|
||||
this.once('load', function() {
|
||||
this.has(hash, noProbe, cb);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res = this.hasBlock(hash) || this.hasOrphan(hash);
|
||||
|
||||
if (!cb)
|
||||
return res;
|
||||
|
||||
cb = utils.asyncify(cb);
|
||||
return cb(res);
|
||||
};
|
||||
|
||||
Chain.prototype.byHeight = function byHeight(height) {
|
||||
if (this.loading)
|
||||
return null;
|
||||
|
||||
return this.index.entries[height];
|
||||
};
|
||||
|
||||
Chain.prototype.byHash = function byHash(hash) {
|
||||
if (this.loading)
|
||||
return null;
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
return this.byHeight(this.index.heights[hash]);
|
||||
};
|
||||
|
||||
Chain.prototype.hasBlock = function hasBlock(hash) {
|
||||
if (this.loading)
|
||||
return false;
|
||||
|
||||
return !!this.byHash(hash);
|
||||
};
|
||||
|
||||
Chain.prototype.hasOrphan = function hasOrphan(hash) {
|
||||
if (this.loading)
|
||||
return false;
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
return !!this.orphan.bmap[hash];
|
||||
};
|
||||
|
||||
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;
|
||||
return delta < 40 * 60;
|
||||
};
|
||||
|
||||
Chain.prototype.fillPercent = function fillPercent() {
|
||||
var total = (+new Date() / 1000 - 40 * 60) - this.index.lastTs;
|
||||
var current = this.index.entries[this.index.entries.length - 1].ts - this.index.lastTs;
|
||||
return Math.max(0, Math.min(current / total, 1));
|
||||
};
|
||||
|
||||
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
|
||||
assert(false);
|
||||
};
|
||||
|
||||
Chain.prototype.getLast = function getLast(cb) {
|
||||
var res;
|
||||
|
||||
if (this.loading) {
|
||||
this.once('load', function() {
|
||||
this.getLast(cb);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res = this.index.hashes[this.index.hashes.length - 1];
|
||||
|
||||
if (!cb)
|
||||
return res;
|
||||
|
||||
cb = utils.asyncify(cb);
|
||||
return cb(res);
|
||||
};
|
||||
|
||||
Chain.prototype.getStartHeight = function getStartHeight() {
|
||||
if (this.loading)
|
||||
return 0;
|
||||
|
||||
return this.index.entries[this.index.entries.length - 1].height;
|
||||
};
|
||||
|
||||
Chain.prototype.locatorHashes = function locatorHashes(start) {
|
||||
var chain = this.index.hashes;
|
||||
var hashes = [];
|
||||
var top = chain.length - 1;
|
||||
var step = 1;
|
||||
var i;
|
||||
|
||||
if (start) {
|
||||
if (Array.isArray(start))
|
||||
start = utils.toHex(start);
|
||||
else if (start.hash)
|
||||
start = start.hash('hex');
|
||||
}
|
||||
|
||||
if (typeof start === 'string') {
|
||||
// Hash
|
||||
if (this.index.heights[start])
|
||||
top = this.index.heights[start];
|
||||
} else if (typeof start === 'number') {
|
||||
// Height
|
||||
top = start;
|
||||
}
|
||||
|
||||
i = top;
|
||||
for (;;) {
|
||||
hashes.push(chain[i]);
|
||||
i = i - step;
|
||||
if (i <= 0) {
|
||||
hashes.push(chain[0]);
|
||||
break;
|
||||
}
|
||||
if (hashes.length >= 10)
|
||||
step *= 2;
|
||||
}
|
||||
|
||||
return hashes;
|
||||
};
|
||||
|
||||
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
|
||||
var self = this;
|
||||
var root = hash;
|
||||
|
||||
if (Array.isArray(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
while (this.orphan.bmap[hash]) {
|
||||
root = hash;
|
||||
hash = this.orphan.bmap[hash].prevBlock;
|
||||
}
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
Chain.prototype.getHeight = function getHeight(hash) {
|
||||
var entry = this.byHash(hash);
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
return entry.height;
|
||||
};
|
||||
|
||||
Chain.prototype.getNextBlock = function getNextBlock(hash) {
|
||||
var entry = this.byHash(hash);
|
||||
|
||||
if (!entry || !entry.next)
|
||||
return null;
|
||||
|
||||
return entry.next.hash;
|
||||
};
|
||||
|
||||
Chain.prototype._save = function(entry) {
|
||||
var self = this;
|
||||
|
||||
if (!this.storage)
|
||||
return;
|
||||
|
||||
this.storage.put(this.prefix + entry.hash, entry.toJSON(), function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype._del = function(entry) {
|
||||
var self = this;
|
||||
|
||||
if (!this.storage)
|
||||
return;
|
||||
|
||||
this.storage.del(this.prefix + entry.hash, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype.toJSON = function toJSON() {
|
||||
var entries = this.index.entries;
|
||||
|
||||
return {
|
||||
v: 1,
|
||||
type: 'chain',
|
||||
network: network.type,
|
||||
entries: entries.map(function(entry) {
|
||||
return entry.toJSON();
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
Chain.prototype.fromJSON = function fromJSON(json) {
|
||||
assert.equal(json.v, 1);
|
||||
assert.equal(json.type, 'chain');
|
||||
assert.equal(json.network, network.type);
|
||||
|
||||
json.entries.forEach(function(entry) {
|
||||
this._addIndex(ChainBlock.fromJSON(this, entry));
|
||||
}, this);
|
||||
|
||||
if (this.index.entries.length === 0)
|
||||
this.add(new bcoin.block(network.genesis, 'block'));
|
||||
};
|
||||
|
||||
/**
|
||||
* ChainBlock
|
||||
*/
|
||||
|
||||
function ChainBlock(chain, data) {
|
||||
this.chain = chain;
|
||||
this.hash = data.hash;
|
||||
this.version = data.version;
|
||||
// this.prevBlock = data.prevBlock;
|
||||
this.ts = data.ts;
|
||||
this.bits = data.bits;
|
||||
this.height = data.height;
|
||||
this.chainwork = this.getChainwork();
|
||||
}
|
||||
|
||||
ChainBlock.prototype.__defineGetter__('prev', function() {
|
||||
return this.chain.index.entries[this.height - 1];
|
||||
});
|
||||
|
||||
ChainBlock.prototype.__defineGetter__('next', function() {
|
||||
return this.chain.index.entries[this.height + 1];
|
||||
});
|
||||
|
||||
ChainBlock.prototype.__defineGetter__('proof', function() {
|
||||
var target = utils.fromCompact(this.bits);
|
||||
if (target.isNeg() || target.cmpn(0) === 0)
|
||||
return new bn(0);
|
||||
// May be faster:
|
||||
// return new bn(1).shln(256).div(target.addn(1));
|
||||
return new bn(2).pow(new bn(256)).div(target.addn(1));
|
||||
});
|
||||
|
||||
ChainBlock.prototype.getChainwork = function() {
|
||||
if (!this.prev)
|
||||
return new bn(0);
|
||||
|
||||
return (this.prev ? this.prev.chainwork : new bn(0)).add(this.proof);
|
||||
};
|
||||
|
||||
ChainBlock.prototype.toJSON = function() {
|
||||
// return [
|
||||
// this.hash,
|
||||
// this.version,
|
||||
// // this.prevBlock,
|
||||
// this.ts,
|
||||
// this.bits,
|
||||
// this.height
|
||||
// };
|
||||
return {
|
||||
hash: this.hash,
|
||||
version: this.version,
|
||||
// prevBlock: this.prevBlock,
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
height: this.height
|
||||
};
|
||||
};
|
||||
|
||||
ChainBlock.fromJSON = function(chain, json) {
|
||||
// return new ChainBlock(chain, {
|
||||
// hash: json[0],
|
||||
// version: json[1],
|
||||
// ts: json[2],
|
||||
// bits: json[3],
|
||||
// height: json[4]
|
||||
// });
|
||||
return new ChainBlock(chain, json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Chain;
|
||||
@ -19,6 +19,7 @@ var network = bcoin.protocol.network;
|
||||
|
||||
function Pool(options) {
|
||||
var self = this;
|
||||
var Chain;
|
||||
|
||||
if (!(this instanceof Pool))
|
||||
return new Pool(options);
|
||||
@ -57,14 +58,22 @@ function Pool(options) {
|
||||
hiReached: false
|
||||
};
|
||||
|
||||
if (this.options.fullNode) {
|
||||
if (!options.loadTimeout)
|
||||
this.load.timeout = 30000;
|
||||
if (!options.loadInterval)
|
||||
this.load.interval = 5000;
|
||||
}
|
||||
|
||||
this.maxRetries = options.maxRetries || 42;
|
||||
this.requestTimeout = options.requestTimeout || 10000;
|
||||
|
||||
this.chain = new bcoin.chain({
|
||||
Chain = this.options.fullNode
|
||||
? bcoin.fullChain
|
||||
: bcoin.spvChain;
|
||||
|
||||
this.chain = new Chain({
|
||||
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.fullNode ? 100 : null,
|
||||
fullNode: this.options.fullNode,
|
||||
startHeight: this.options.startHeight
|
||||
});
|
||||
@ -77,11 +86,6 @@ function Pool(options) {
|
||||
(Math.random() * 0xffffffff) | 0
|
||||
);
|
||||
|
||||
this.bestHeight = 0;
|
||||
this.bestBlock = null;
|
||||
this.needSync = true;
|
||||
this.syncPeer = null;
|
||||
|
||||
this.peers = {
|
||||
// Peers that are loading blocks themselves
|
||||
block: [],
|
||||
@ -92,7 +96,9 @@ function Pool(options) {
|
||||
};
|
||||
|
||||
this.block = {
|
||||
lastHash: null
|
||||
lastHash: null,
|
||||
bestHeight: 0,
|
||||
bestHash: null
|
||||
};
|
||||
|
||||
this.request = {
|
||||
@ -153,19 +159,23 @@ Pool.prototype._init = function _init() {
|
||||
this._load();
|
||||
|
||||
this.chain.on('missing', function(hash, preload, parent) {
|
||||
if (self.options.fullNode) return;
|
||||
self._request('block', hash, { force: true });
|
||||
self._scheduleRequests();
|
||||
self._loadRange(preload);
|
||||
if (!self.options.fullNode) {
|
||||
self._request('filtered', hash, { force: true });
|
||||
self._scheduleRequests();
|
||||
self._loadRange(preload);
|
||||
}
|
||||
});
|
||||
|
||||
this.chain.on('fork', function(height, hash, checkpoint) {
|
||||
var peer = self.syncPeer;
|
||||
var peer = self.peers.load;
|
||||
|
||||
if (!self.options.fullNode)
|
||||
return;
|
||||
|
||||
if (!peer)
|
||||
return;
|
||||
delete self.syncPeer;
|
||||
|
||||
peer.destroy();
|
||||
self.startSync();
|
||||
});
|
||||
|
||||
this.options.wallets.forEach(function(w) {
|
||||
@ -180,7 +190,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
if (this.destroyed)
|
||||
return;
|
||||
|
||||
if (this.peers.load !== null)
|
||||
if (this.peers.load != null)
|
||||
return;
|
||||
|
||||
peer = new bcoin.peer(this, this.createSocket, {
|
||||
@ -188,6 +198,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
startHeight: this.options.startHeight,
|
||||
relay: this.options.relay
|
||||
});
|
||||
|
||||
this.peers.load = peer;
|
||||
|
||||
peer.on('error', function(err) {
|
||||
@ -204,18 +215,23 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
self._addLoader();
|
||||
}
|
||||
|
||||
interval = setInterval(function() {
|
||||
self._load();
|
||||
}, this.load.interval);
|
||||
|
||||
peer.once('ack', function() {
|
||||
peer.updateWatch();
|
||||
if (!self._load())
|
||||
clearTimeout(timer);
|
||||
});
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
peer.on('version', function(version) {
|
||||
if (version.height > self.block.bestHeight)
|
||||
self.block.bestHeight = version.height;
|
||||
self.emit('version', version, peer);
|
||||
});
|
||||
|
||||
function load() {
|
||||
self._load();
|
||||
}
|
||||
|
||||
interval = setInterval(load, this.load.interval);
|
||||
|
||||
function destroy() {
|
||||
// Chain is full and up-to-date
|
||||
@ -231,44 +247,119 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
|
||||
timer = setTimeout(destroy, this.load.timeout);
|
||||
|
||||
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.emit('blocks', hashes);
|
||||
|
||||
if (hashes.length === 0) {
|
||||
// Reset global load
|
||||
self.block.lastHash = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Request each block
|
||||
hashes.forEach(function(hash) {
|
||||
self._request('block', hash);
|
||||
});
|
||||
if (!self.options.fullNode) {
|
||||
// Request each block
|
||||
hashes.forEach(function(hash) {
|
||||
self._request('filtered', hash);
|
||||
});
|
||||
|
||||
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];
|
||||
if (self.chain.hasOrphan(hash)) {
|
||||
peer.loadBlocks(self.chain.locatorHashes(), self.chain.getOrphanRoot(hash));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i === hashes.length - 1)
|
||||
peer.loadBlocks(self.chain.locatorHashes(), 0);
|
||||
|
||||
if (!self.chain.hasBlock(hash))
|
||||
self._request('block', hash);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
self.block.lastHash = hashes[hashes.length - 1];
|
||||
|
||||
// Store last hash to continue global load
|
||||
self.block.lastHash = hashes[hashes.length - 1];
|
||||
clearInterval(interval);
|
||||
interval = setInterval(load, self.load.interval);
|
||||
|
||||
clearTimeout(timer);
|
||||
|
||||
// Reinstantiate timeout
|
||||
if (self._load())
|
||||
timer = setTimeout(destroy, self.load.timeout);
|
||||
});
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(destroy, self.load.timeout);
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype._handleMerkle = function _handleMerkle(block, peer) {
|
||||
this._response(block);
|
||||
this.chain.add(block);
|
||||
this.emit('chain-progress', this.chain.fillPercent(), peer);
|
||||
this.emit('block', block, peer);
|
||||
};
|
||||
|
||||
Pool.prototype._handleBlock = function _handleBlock(block, peer) {
|
||||
var chainIndex = this.chain.index;
|
||||
var hash, len, orphan, err;
|
||||
|
||||
backoff = 0;
|
||||
|
||||
this._response(block);
|
||||
|
||||
hash = block.hash('hex');
|
||||
len = chainIndex.hashes.length;
|
||||
orphan = this.chain.hasOrphan(block);
|
||||
|
||||
err = this.chain.add(block);
|
||||
if (err)
|
||||
this.emit('chain-error', err, peer);
|
||||
|
||||
this.emit('_block', block, peer);
|
||||
|
||||
if (this.chain.hasOrphan(block)) {
|
||||
peer.loadBlocks(this.chain.locatorHashes(), this.chain.getOrphanRoot(block));
|
||||
if (!orphan)
|
||||
this.emit('orphan', block, peer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chainIndex.hashes.length === len)
|
||||
return;
|
||||
|
||||
this.emit('chain-progress', this.chain.fillPercent(), peer);
|
||||
this.emit('block', block, peer);
|
||||
};
|
||||
|
||||
Pool.prototype.isFull = function isFull() {
|
||||
return this.chain.isFull();
|
||||
};
|
||||
@ -304,9 +395,7 @@ Pool.prototype._loadRange = function _loadRange(hashes, force) {
|
||||
|
||||
Pool.prototype._load = function _load() {
|
||||
var self = this;
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
var next;
|
||||
|
||||
if (this.request.queue.length >= this.load.hwm) {
|
||||
this.load.hiReached = true;
|
||||
@ -315,19 +404,32 @@ Pool.prototype._load = function _load() {
|
||||
|
||||
this.load.hiReached = false;
|
||||
|
||||
// Load more blocks, starting from last hash
|
||||
if (this.block.lastHash)
|
||||
next(this.block.lastHash);
|
||||
else
|
||||
this.chain.getLast(next);
|
||||
console.log(
|
||||
'Requesting inv packet from %s with getblocks',
|
||||
this.peers.load.address);
|
||||
|
||||
function next(hash) {
|
||||
if (!self.peers.load)
|
||||
self._addLoader();
|
||||
if (!this.options.fullNode) {
|
||||
next = function(hash) {
|
||||
if (!self.peers.load)
|
||||
self._addLoader();
|
||||
else
|
||||
self.peers.load.loadBlocks([ hash ]);
|
||||
};
|
||||
|
||||
// Load more blocks, starting from last hash
|
||||
if (this.block.lastHash)
|
||||
next(this.block.lastHash);
|
||||
else
|
||||
self.peers.load.loadBlocks([ hash ]);
|
||||
this.chain.getLast(next);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.peers.load)
|
||||
this._addLoader();
|
||||
else
|
||||
this.peers.load.loadBlocks(this.chain.locatorHashes(), 0);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -392,48 +494,13 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
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);
|
||||
self._handleMerkle(block, peer);
|
||||
});
|
||||
} else {
|
||||
peer.on('block', function(block) {
|
||||
var hashes = self.chain.index.hashes;
|
||||
var hash, len, orphan, err;
|
||||
|
||||
if (self.syncPeer !== peer)
|
||||
return;
|
||||
|
||||
// Reset backoff, peer seems to be responsive
|
||||
backoff = 0;
|
||||
|
||||
self._response(block);
|
||||
|
||||
hash = block.hash('hex');
|
||||
len = hashes.length;
|
||||
orphan = self.chain.hasOrphan(block);
|
||||
|
||||
err = self.chain.add(block);
|
||||
if (err)
|
||||
self.emit('chain-error', err, peer);
|
||||
|
||||
self.emit('_block', block, peer);
|
||||
|
||||
if (self.chain.hasOrphan(block)) {
|
||||
peer.loadBlocks(self.chain.locatorHashes(), self.chain.getOrphanRoot(block));
|
||||
if (!orphan)
|
||||
self.emit('orphan', block, peer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hashes.length === len)
|
||||
return;
|
||||
|
||||
self.needSync = hashes[hashes.length - 1] !== self.bestBlock;
|
||||
|
||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||
self.emit('block', block, peer);
|
||||
self._handleBlock(block, peer);
|
||||
});
|
||||
}
|
||||
|
||||
@ -461,7 +528,7 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
|
||||
peer.on('blocks', function(blocks) {
|
||||
if (blocks.length === 1)
|
||||
self.bestBlock = peer.bestBlock;
|
||||
self.block.bestHash = peer.bestBlock;
|
||||
self.emit('blocks', blocks, peer);
|
||||
});
|
||||
|
||||
@ -470,80 +537,26 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
});
|
||||
|
||||
peer.on('version', function(version) {
|
||||
if (version.height > self.bestHeight)
|
||||
self.bestHeight = version.height;
|
||||
if (version.height > self.block.bestHeight)
|
||||
self.block.bestHeight = version.height;
|
||||
self.emit('version', version, peer);
|
||||
});
|
||||
|
||||
peer.on('ack', function() {
|
||||
if (self.options.fullNode) {
|
||||
if (self.peers.block.length >= Math.min(5, self.size))
|
||||
self.startSync();
|
||||
}
|
||||
});
|
||||
|
||||
utils.nextTick(function() {
|
||||
self.emit('peer', peer);
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype.bestPeer = function bestPeer() {
|
||||
var best = null;
|
||||
|
||||
this.peers.block.forEach(function(peer) {
|
||||
return this.peers.block.reduce(function(best, peer) {
|
||||
if (!peer.version || !peer.socket)
|
||||
return;
|
||||
|
||||
if (!best || peer.version.height > best.version.height)
|
||||
best = peer;
|
||||
});
|
||||
return peer;
|
||||
|
||||
if (best)
|
||||
this.emit('debug', 'Best peer: %s', best.address);
|
||||
|
||||
return best;
|
||||
};
|
||||
|
||||
Pool.prototype.startSync = function startSync(peer) {
|
||||
if (!this.options.fullNode)
|
||||
return;
|
||||
|
||||
if (this.syncPeer)
|
||||
return;
|
||||
|
||||
peer = peer || this.bestPeer();
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
this.syncPeer = peer;
|
||||
|
||||
peer.on('blocks', function(hashes) {
|
||||
var req = [];
|
||||
var i, hash;
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
if (peer.chain.hasOrphan(hash)) {
|
||||
peer.loadBlocks(peer.chain.locatorHashes(), peer.chain.getOrphanRoot(hash));
|
||||
continue;
|
||||
}
|
||||
if (!peer.chain.hasBlock(hash)) {
|
||||
req.push({ type: 'block', hash: hash });
|
||||
continue;
|
||||
}
|
||||
if (i === hashes.length - 1) {
|
||||
peer.loadBlocks(peer.chain.locatorHashes(), 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.length)
|
||||
peer.getData(req);
|
||||
});
|
||||
|
||||
this.emit('debug', 'version (%s): sending locator hashes', peer.address);
|
||||
|
||||
peer.loadBlocks(this.chain.locatorHashes(), 0);
|
||||
return best;
|
||||
}, null);
|
||||
};
|
||||
|
||||
Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
@ -557,16 +570,14 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
|
||||
if (this.peers.load === peer)
|
||||
this.peers.load = null;
|
||||
|
||||
if (this.syncPeer === peer) {
|
||||
delete this.syncPeer;
|
||||
this.startSync();
|
||||
}
|
||||
};
|
||||
|
||||
Pool.prototype.watch = function watch(id) {
|
||||
var hid, i;
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
if (id instanceof bcoin.wallet) {
|
||||
this.watchWallet(id);
|
||||
return;
|
||||
@ -595,6 +606,9 @@ Pool.prototype.watch = function watch(id) {
|
||||
Pool.prototype.unwatch = function unwatch(id) {
|
||||
var i;
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
id = utils.toHex(id);
|
||||
|
||||
if (!this.bloom.test(id, 'hex'))
|
||||
@ -648,6 +662,9 @@ Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
||||
if (!ts)
|
||||
ts = defaultTs || ((+new Date / 1000) - 7 * 24 * 3600);
|
||||
|
||||
if (self.options.fullNode)
|
||||
return;
|
||||
|
||||
self.search(false, ts, e);
|
||||
}
|
||||
|
||||
@ -663,12 +680,16 @@ Pool.prototype.removeWallet = function removeWallet(w) {
|
||||
};
|
||||
|
||||
Pool.prototype.watchWallet = function watchWallet(w) {
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
if (w.type === 'scripthash') {
|
||||
// For the redeem script hash in outputs:
|
||||
this.watch(w.getFullHash());
|
||||
// For the redeem script in inputs:
|
||||
this.watch(w.getFullPublicKey());
|
||||
}
|
||||
|
||||
// For the pubkey hash in outputs:
|
||||
this.watch(w.getOwnHash());
|
||||
// For the pubkey in inputs:
|
||||
@ -676,12 +697,16 @@ Pool.prototype.watchWallet = function watchWallet(w) {
|
||||
};
|
||||
|
||||
Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
if (w.type === 'scripthash') {
|
||||
// For the redeem script hash in p2sh outputs:
|
||||
this.unwatch(w.getFullHash());
|
||||
// For the redeem script in p2sh inputs:
|
||||
this.unwatch(w.getFullPublicKey());
|
||||
}
|
||||
|
||||
// For the pubkey hash in p2pk/multisig outputs:
|
||||
this.unwatch(w.getOwnHash());
|
||||
// For the pubkey in p2pkh inputs:
|
||||
@ -691,6 +716,9 @@ Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
||||
Pool.prototype.search = function search(id, range, e) {
|
||||
var self = this;
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
e = e || new EventEmitter();
|
||||
|
||||
// Optional id argument
|
||||
@ -726,6 +754,8 @@ Pool.prototype.search = function search(id, range, e) {
|
||||
self.watch(id);
|
||||
|
||||
self._loadRange(hashes, true);
|
||||
// XXX
|
||||
// return;
|
||||
hashes = hashes.slice().reverse();
|
||||
hashes.forEach(function(hash, i) {
|
||||
// Get the block that is in index
|
||||
@ -802,7 +832,7 @@ Pool.prototype._request = function _request(type, hash, options, cb) {
|
||||
}
|
||||
|
||||
// Block should be not in chain, or be requested
|
||||
if (!options.force && type === 'block')
|
||||
if (!options.force && (type === 'block' || type === 'filtered'))
|
||||
return this.chain.has(hash, true, next);
|
||||
|
||||
return next(false);
|
||||
@ -846,6 +876,7 @@ Pool.prototype._scheduleRequests = function _scheduleRequests() {
|
||||
Pool.prototype._doRequests = function _doRequests() {
|
||||
var queue, above, items, below;
|
||||
var red, count, split, i, off, req, j;
|
||||
var mapReq;
|
||||
|
||||
if (this.request.active >= this.parallel)
|
||||
return;
|
||||
@ -864,19 +895,29 @@ Pool.prototype._doRequests = function _doRequests() {
|
||||
if (above && below && this.load.hiReached)
|
||||
this._load();
|
||||
|
||||
function mapReq(item) {
|
||||
return item.start(this.peers.block[i]);
|
||||
if (!this.options.fullNode) {
|
||||
mapReq = function(item) {
|
||||
return item.start(this.peers.block[i]);
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
req = items.map(function(item) {
|
||||
return item.start(this.peers.load);
|
||||
}, this);
|
||||
|
||||
this.peers.load.getData(req);
|
||||
};
|
||||
|
||||
Pool.prototype.getTX = function getTX(hash, range, cb) {
|
||||
@ -1056,13 +1097,8 @@ LoadRequest.prototype.start = function start(peer) {
|
||||
this.peer = peer;
|
||||
this.peer.once('close', this.onclose);
|
||||
|
||||
if (this.type === 'block')
|
||||
reqType = 'filtered';
|
||||
else if (this.type === 'tx')
|
||||
reqType = 'tx';
|
||||
|
||||
return {
|
||||
type: reqType,
|
||||
type: this.type,
|
||||
hash: this.hash
|
||||
};
|
||||
};
|
||||
|
||||
@ -5,14 +5,7 @@ var net = require('net');
|
||||
var path = require('path');
|
||||
var bcoin = require('../');
|
||||
|
||||
var addrs = [
|
||||
'seed.bitcoin.sipa.be',
|
||||
'dnsseed.bluematt.me',
|
||||
'dnsseed.bitcoin.dashjr.org',
|
||||
'seed.bitcoinstats.com',
|
||||
'seed.bitnodes.io',
|
||||
'bitseed.xf2.org'
|
||||
];
|
||||
var addrs = bcoin.protocol.network.seeds.slice();
|
||||
|
||||
var pool = bcoin.pool({
|
||||
size: 32,
|
||||
@ -25,6 +18,8 @@ var pool = bcoin.pool({
|
||||
}
|
||||
});
|
||||
|
||||
pool.on('error', function() {});
|
||||
|
||||
console.log('Updating bcoin preloaded chain...');
|
||||
|
||||
pool.on('block', function(block) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user