misc method improvements.

This commit is contained in:
Christopher Jeffrey 2016-02-19 02:53:33 -08:00
parent 71557ba95c
commit 9bc3e44a7f
7 changed files with 121 additions and 172 deletions

View File

@ -347,20 +347,6 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
return height;
};
Block.prototype.getNextBlock = function getNextBlock() {
var next;
if (!this.chain)
return utils.toHex(constants.zeroHash);
next = this.chain.getNextBlock(this.hash('hex'));
if (!next)
return utils.toHex(constants.zeroHash);
return next;
};
Block.reward = function reward(height) {
var halvings = height / network.halvingInterval | 0;
var reward;
@ -432,7 +418,7 @@ Block.prototype.getFee = function getFee() {
Block.prototype.getEntry = function getEntry() {
if (!this.chain)
return;
return this.chain.getBlock(this);
return this.chain.getEntry(this);
};
Block.prototype.isOrphan = function isOrphan() {
@ -462,10 +448,6 @@ Block.prototype.__defineGetter__('rhash', function() {
return utils.revHex(this.hash('hex'));
});
Block.prototype.__defineGetter__('nextBlock', function() {
return this.getNextBlock();
});
Block.prototype.__defineGetter__('reward', function() {
return this.getReward();
});
@ -493,7 +475,6 @@ Block.prototype.inspect = function inspect() {
delete copy._chain;
copy.hash = this.hash('hex');
copy.rhash = this.rhash;
copy.nextBlock = this.getNextBlock();
copy.reward = utils.btc(this.getReward());
copy.fee = utils.btc(this.getFee());
copy.date = new Date((copy.ts || 0) * 1000).toISOString();

View File

@ -737,7 +737,7 @@ BlockDB.prototype._getTXByAddress = function _getTXByAddress(address, callback)
if (data) {
try {
tx = bcoin.tx.fromRaw(data);
entry = bcoin.chain.global.db.get(record.height);
entry = bcoin.chain.global.db.getSync(record.height);
assert(entry);
} catch (e) {
return callback(e);
@ -797,7 +797,7 @@ BlockDB.prototype.getTX = function getTX(hash, callback) {
if (data) {
try {
tx = bcoin.tx.fromRaw(data);
entry = bcoin.chain.global.db.get(record.height);
entry = bcoin.chain.global.db.getSync(record.height);
assert(entry);
} catch (e) {
return callback(e);
@ -847,7 +847,7 @@ BlockDB.prototype.getBlock = function getBlock(hash, callback) {
block.height = record.height;
if (self.options.paranoid) {
if (typeof hash === 'number') {
hash = bcoin.chain.global.get(hash);
hash = bcoin.chain.global.db.getSync(hash);
if (!hash)
return callback(null, block);
hash = hash.hash;
@ -1051,7 +1051,7 @@ function BlockData(options) {
if (!this.file)
this.file = process.env.HOME + '/bcoin-block-' + network.type + '.db';
this._bufferPool = { used: {} };
this.bufferPool = { used: {} };
this.size = 0;
this.fd = null;
@ -1076,7 +1076,7 @@ BlockData.prototype._init = function _init() {
if (!this.exists())
fs.writeFileSync(this.file, new Buffer([]));
this.size = this.getSize();
this.size = this.getFileSize();
this.fd = fs.openSync(this.file, 'r+');
};
@ -1115,24 +1115,24 @@ BlockData.prototype._malloc = function _malloc(size) {
if (size > 500)
return new Buffer(size);
if (!this._bufferPool[size])
this._bufferPool[size] = new Buffer(size);
if (!this.bufferPool[size])
this.bufferPool[size] = new Buffer(size);
if (this._bufferPool.used[size] === this._bufferPool[size])
if (this.bufferPool.used[size] === this.bufferPool[size])
return new Buffer(size);
this._bufferPool.used[size] = this._bufferPool[size];
this.bufferPool.used[size] = this.bufferPool[size];
return this._bufferPool[size];
return this.bufferPool[size];
};
BlockData.prototype._free = function _free(buf) {
if (!this.options.usePool)
return;
if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length];
if (this.bufferPool.used[buf.length] === buf) {
assert(this.bufferPool[buf.length] === buf);
delete this.bufferPool.used[buf.length];
}
};
@ -1148,7 +1148,7 @@ BlockData.prototype.exists = function exists() {
}
};
BlockData.prototype.getSize = function getSize() {
BlockData.prototype.getFileSize = function getFileSize() {
if (!bcoin.fs)
return this.ramdisk.size;
@ -1277,6 +1277,9 @@ BlockData.prototype._readAsync = function _readAsync(size, offset, callback) {
return callback(err);
}
if (!bytes)
return callback(new Error('_readAsync() failed.'));
index += bytes;
size -= bytes;
offset += bytes;
@ -1349,6 +1352,9 @@ BlockData.prototype._writeAsync = function _writeAsync(data, offset, callback) {
return callback(err);
}
if (!bytes)
return callback(new Error('_writeAsync() failed.'));
index += bytes;
size -= bytes;
offset += bytes;

View File

@ -233,7 +233,7 @@ Chain.prototype._preload = function _preload(callback) {
utils.debug('Loading %s', url);
stream = request.get(url);
chainHeight = this.db.count() - 1;
chainHeight = this.db.getSize() - 1;
height = 0;
buf = {
data: [],
@ -249,7 +249,7 @@ Chain.prototype._preload = function _preload(callback) {
stream.on('error', function(err) {
var start = Math.max(0, height - 2);
self.resetHeight(start);
self.resetHeightSync(start);
return callback(err, start + 1);
});
@ -285,7 +285,7 @@ Chain.prototype._preload = function _preload(callback) {
if (lastEntry && entry.prevBlock !== lastEntry.hash) {
start = Math.max(0, height - 2);
stream.destroy();
self.resetHeight(start);
self.resetHeightSync(start);
return callback(new Error('Corrupt headers.'), start + 1);
}
@ -294,7 +294,7 @@ Chain.prototype._preload = function _preload(callback) {
if (!block.verify()) {
start = Math.max(0, height - 2);
stream.destroy();
self.resetHeight(start);
self.resetHeightSync(start);
return callback(new Error('Bad headers.'), start + 1);
}
@ -311,10 +311,12 @@ Chain.prototype._preload = function _preload(callback) {
// Filthy hack to avoid writing
// redundant blocks to disk!
if (height < chainHeight)
if (height <= chainHeight) {
self.db._cache(entry);
self.db._populate(entry);
else
self.db.save(entry);
} else {
self.db.saveAsync(entry);
}
height++;
@ -666,7 +668,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
}
// Duplicate height (do a sync call here since this is cached)
existing = this.db.get(entry.height);
existing = this.db.getSync(entry.height);
if (existing && existing.hash === entry.hash)
return callback(null, false);
@ -674,7 +676,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
if (err)
return callback(err);
self.db.save(entry, function(err) {
self.db.saveAsync(entry, function(err) {
if (err)
return callback(err);
@ -694,7 +696,7 @@ Chain.prototype.resetHeight = function resetHeight(height) {
if (height === count - 1)
return;
this.db.resetHeight(height);
this.db.resetHeightSync(height);
// Reset the orphan map completely. There may
// have been some orphans on a forked chain we
@ -753,7 +755,7 @@ Chain.prototype.revertHeight = function revertHeight(height, callback) {
callback(err, result);
}
chainHeight = this.db.count() - 1;
chainHeight = this.db.getSize() - 1;
if (chainHeight < 0)
return done(new Error('Bad chain height.'));
@ -824,7 +826,7 @@ Chain.prototype.syncHeight = function syncHeight(callback) {
callback(err, result);
}
chainHeight = this.db.count() - 1;
chainHeight = this.db.getSize() - 1;
if (chainHeight < 0)
return done(new Error('Bad chain height.'));
@ -867,7 +869,7 @@ Chain.prototype.resetTime = function resetTime(ts) {
var entry = this.byTime(ts);
if (!entry)
return;
return this.resetHeight(entry.height);
return this.resetHeightSync(entry.height);
};
Chain.prototype.resetTimeAsync = function resetTimeAsync(ts, callback) {
@ -1114,7 +1116,7 @@ Chain.prototype.add = function add(initial, peer, callback) {
// Lookup previous entry.
// We can do this synchronously:
// it is already cached.
prev = self.db.get(prevHeight);
prev = self.db.getSync(prevHeight);
assert(prev);
// Do "contextual" verification on our block
@ -1258,7 +1260,7 @@ Chain.prototype.byTime = function byTime(ts) {
// timestamp.
while (start < end) {
pos = (start + end) >> 1;
entry = this.db.get(pos);
entry = this.db.getSync(pos);
delta = Math.abs(ts - entry.ts);
if (delta <= 60 * 60)
@ -1271,7 +1273,7 @@ Chain.prototype.byTime = function byTime(ts) {
}
}
return this.db.get(start);
return this.db.getSync(start);
};
Chain.prototype.byTimeAsync = function byTimeAsync(ts, callback) {
@ -1349,19 +1351,19 @@ Chain.prototype.hasPending = function hasPending(hash) {
return !!this.pendingBlocks[hash];
};
Chain.prototype.getBlock = function getBlock(hash) {
Chain.prototype.getEntry = function getEntry(hash) {
if (typeof hash === 'number')
return this.db.get(hash);
return this.db.getSync(hash);
if (utils.isBuffer(hash))
hash = utils.toHex(hash);
else if (hash.hash)
hash = hash.hash('hex');
return this.db.get(hash);
return this.db.getSync(hash);
};
Chain.prototype.getBlockAsync = function getBlockAsync(hash, callback) {
Chain.prototype.getEntryAsync = function getEntryAsync(hash, callback) {
if (typeof hash === 'number')
return this.db.getAsync(hash, callback);
@ -1399,26 +1401,23 @@ Chain.prototype.getProgress = function getProgress() {
return Math.min(1, this.tip.ts / (utils.now() - 40 * 60));
};
Chain.prototype.hashRange = function hashRange(start, end) {
Chain.prototype.getHashRange = function getHashRange(start, end) {
var hashes = [];
var i;
start = this.byTime(start);
end = this.byTime(end);
if (!end)
end = this.tip;
if (!start || !end)
return hashes;
for (i = start.height; i < end.height + 1; i++)
hashes.push(this.db.get(i).hash);
hashes.push(this.db.getSync(i).hash);
return hashes;
};
Chain.prototype.hashRangeAsync = function hashRangeAsync(start, end, callback) {
Chain.prototype.getHashRangeAsync = function getHashRangeAsync(start, end, callback) {
var self = this;
var called;
@ -1441,9 +1440,6 @@ Chain.prototype.hashRangeAsync = function hashRangeAsync(start, end, callback) {
hashes = [];
if (!end)
end = self.tip;
if (!start || !end)
return done(null, hashes);
@ -1460,7 +1456,7 @@ Chain.prototype.hashRangeAsync = function hashRangeAsync(start, end, callback) {
if (!entry)
return done(new Error('No entry for hash range.'));
hashes.push(entry.hash);
hashes[i - start.height] = entry.hash;
if (!--pending)
return done(null, hashes);
@ -1503,7 +1499,7 @@ Chain.prototype.getLocator = function getLocator(start) {
i = top;
for (;;) {
existing = this.db.get(i);
existing = this.db.getSync(i);
assert(existing);
hashes.push(existing.hash);
i = i - step;
@ -1624,41 +1620,8 @@ Chain.prototype.getHeight = function getHeight(hash) {
return this.db.getHeight(hash);
};
Chain.prototype.getNextBlock = function getNextBlock(hash) {
var height = this.db.getHeight(hash);
var entry;
if (height === -1)
return;
entry = this.db.get(height + 1);
if (!entry)
return;
return entry.hash;
};
Chain.prototype.getNextBlockAsync = function getNextBlockAsync(hash, callback) {
var height = this.db.getHeight(hash);
if (height === -1)
return callback();
return this.db.getAsync(height + 1, function(err, entry) {
if (err)
return callback(err);
if (!entry)
return callback();
return callback(null, entry.hash);
});
};
Chain.prototype.getSize = function getSize() {
return this.db.count();
return this.db.getSize();
};
// Legacy
@ -1702,7 +1665,7 @@ Chain.prototype.getTarget = function getTarget(last, block) {
// Back 2 weeks
// NOTE: This is cached.
first = this.db.get(last.height - (network.powDiffInterval - 1));
first = this.db.getSync(last.height - (network.powDiffInterval - 1));
assert(first);

View File

@ -38,11 +38,11 @@ function ChainBlock(chain, data) {
ChainBlock.BLOCK_SIZE = 112;
ChainBlock.prototype.__defineGetter__('prev', function() {
return this.chain.db.get(this.height - 1);
return this.chain.db.getSync(this.height - 1);
});
ChainBlock.prototype.__defineGetter__('next', function() {
return this.chain.db.get(this.height + 1);
return this.chain.db.getSync(this.height + 1);
});
ChainBlock.prototype.getProof = function getProof() {
@ -53,13 +53,19 @@ ChainBlock.prototype.getProof = function getProof() {
};
ChainBlock.prototype.getChainwork = function getChainwork() {
var prev;
// Workaround for genesis block
// being added _in_ chaindb.
if (!this.chain.db)
return;
var prev = this.prev;
prev = this.prev;
return (prev ? prev.chainwork : new bn(0)).add(this.getProof());
};
ChainBlock.prototype.isGenesis = function isGenesis(version) {
ChainBlock.prototype.isGenesis = function isGenesis() {
return this.hash === network.genesis.hash;
};

View File

@ -38,9 +38,9 @@ function ChainDB(chain, options) {
this.file = process.env.HOME + '/bcoin-chain-' + network.type + '.db';
this.heightLookup = {};
this._queue = {};
this._cache = {};
this._bufferPool = { used: {} };
this.queue = {};
this.cache = {};
this.bufferPool = { used: {} };
this.highest = -1;
this.tip = null;
this.height = -1;
@ -90,12 +90,12 @@ ChainDB.prototype._init = function _init() {
if (!this.exists())
fs.writeFileSync(this.file, new Buffer([]));
this.size = this.getSize();
this.size = this.getFileSize();
if (this.size % BLOCK_SIZE !== 0) {
utils.debug('Blockchain is at an odd length. Truncating.');
fs.truncateSync(this.file, this.size - (this.size % BLOCK_SIZE));
this.size = this.getSize();
this.size = this.getFileSize();
assert(this.size % BLOCK_SIZE === 0);
}
@ -111,7 +111,7 @@ ChainDB.prototype._init = function _init() {
ChainDB.prototype.load = function load(start, callback) {
var self = this;
var count = this.count();
var count = this.getSize();
var i = start || 0;
var lastEntry;
@ -122,7 +122,7 @@ ChainDB.prototype.load = function load(start, callback) {
utils.debug(
'Blockchain is corrupt after height %d. Resetting.',
height);
self.resetHeight(height);
self.resetHeightSync(height);
} else {
utils.debug('Chain successfully loaded.');
}
@ -156,7 +156,7 @@ ChainDB.prototype.closeSync = function closeSync() {
this.ramdisk = null;
return;
}
fs.closeSync(this.fd);
fs.close(this.fd);
this.fd = null;
};
@ -182,24 +182,24 @@ ChainDB.prototype._malloc = function _malloc(size) {
if (!this.options.usePool)
return new Buffer(size);
if (!this._bufferPool[size])
this._bufferPool[size] = new Buffer(size);
if (!this.bufferPool[size])
this.bufferPool[size] = new Buffer(size);
if (this._bufferPool.used[size] === this._bufferPool[size])
if (this.bufferPool.used[size] === this.bufferPool[size])
return new Buffer(size);
this._bufferPool.used[size] = this._bufferPool[size];
this.bufferPool.used[size] = this.bufferPool[size];
return this._bufferPool[size];
return this.bufferPool[size];
};
ChainDB.prototype._free = function _free(buf) {
if (!this.options.usePool)
return;
if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length];
if (this.bufferPool.used[buf.length] === buf) {
assert(this.bufferPool[buf.length] === buf);
delete this.bufferPool.used[buf.length];
}
};
@ -215,7 +215,7 @@ ChainDB.prototype.exists = function exists() {
}
};
ChainDB.prototype.getSize = function getSize() {
ChainDB.prototype.getFileSize = function getFileSize() {
if (!bcoin.fs)
return this.ramdisk.size;
@ -226,18 +226,18 @@ ChainDB.prototype.getSize = function getSize() {
}
};
ChainDB.prototype.count = function count() {
ChainDB.prototype.getSize = function getSize() {
var len = this.size / BLOCK_SIZE;
assert(len % 1 === 0);
return len;
};
ChainDB.prototype.cache = function cache(entry) {
ChainDB.prototype._cache = function _cache(entry) {
if (entry.height > this.highest) {
this.highest = entry.height;
delete this._cache[entry.height - this._cacheWindow];
this._cache[entry.height] = entry;
assert(Object.keys(this._cache).length <= this._cacheWindow);
delete this.cache[entry.height - this._cacheWindow];
this.cache[entry.height] = entry;
assert(Object.keys(this.cache).length <= this._cacheWindow);
}
};
@ -260,21 +260,17 @@ ChainDB.prototype._populate = function _populate(entry) {
}
};
ChainDB.prototype.get = function get(height) {
return this.getSync(height);
};
ChainDB.prototype.getSync = function getSync(height) {
var data, entry;
if (typeof height === 'string')
height = this.heightLookup[height];
if (this._cache[height])
return this._cache[height];
if (this.cache[height])
return this.cache[height];
if (this._queue[height])
return this._queue[height];
if (this.queue[height])
return this.queue[height];
if (height < 0 || height == null)
return;
@ -289,11 +285,12 @@ ChainDB.prototype.getSync = function getSync(height) {
entry = bcoin.chainblock.fromRaw(this.chain, height, data);
// Populate the entry.
this._populate(entry);
// Cache the past 1001 blocks in memory
// (necessary for isSuperMajority)
this.cache(entry);
this._cache(entry);
return entry;
};
@ -306,11 +303,11 @@ ChainDB.prototype.getAsync = function getAsync(height, callback) {
if (typeof height === 'string')
height = this.heightLookup[height];
if (this._cache[height])
return callback(null, this._cache[height]);
if (this.cache[height])
return callback(null, this.cache[height]);
if (this._queue[height])
return callback(null, this._queue[height]);
if (this.queue[height])
return callback(null, this.queue[height]);
if (height < 0 || height == null)
return callback();
@ -321,31 +318,25 @@ ChainDB.prototype.getAsync = function getAsync(height, callback) {
return this._readAsync(BLOCK_SIZE, height * BLOCK_SIZE, function(err, data) {
var entry;
// We can't ensure the integrity of
// the chain if we get an error.
// Just throw.
if (err)
throw err;
return callback(err);
if (!data)
return callback();
entry = bcoin.chainblock.fromRaw(self.chain, height, data);
// Populate the entry.
self._populate(entry);
// Cache the past 1001 blocks in memory
// (necessary for isSuperMajority)
self.cache(entry);
self._cache(entry);
return callback(null, entry);
});
};
ChainDB.prototype.save = function save(entry, callback) {
return this.saveAsync(entry, callback);
};
ChainDB.prototype.saveSync = function saveSync(entry) {
var raw, offset;
@ -354,8 +345,9 @@ ChainDB.prototype.saveSync = function saveSync(entry) {
// Cache the past 1001 blocks in memory
// (necessary for isSuperMajority)
this.cache(entry);
this._cache(entry);
// Populate the entry.
this._populate(entry);
raw = entry.toRaw();
@ -375,34 +367,32 @@ ChainDB.prototype.saveAsync = function saveAsync(entry, callback) {
// Cache the past 1001 blocks in memory
// (necessary for isSuperMajority)
this.cache(entry);
this._cache(entry);
// Populate the entry.
this._populate(entry);
// Something is already writing. Cancel it
// and synchronously write the data after
// it cancels.
if (this._queue[entry.height]) {
this._queue[entry.height] = entry;
if (this.queue[entry.height]) {
this.queue[entry.height] = entry;
return callback();
}
// Speed up writes by doing them asynchronously
// and keeping the data to be written in memory.
this._queue[entry.height] = entry;
this.queue[entry.height] = entry;
// Write asynchronously to the db.
raw = entry.toRaw();
offset = entry.height * BLOCK_SIZE;
return this._writeAsync(raw, offset, function(err, success) {
// We can't ensure the integrity of
// the chain if we get an error.
// Just throw.
if (err)
throw err;
return callback(err);
var item = self._queue[entry.height];
var item = self.queue[entry.height];
// Something tried to write here but couldn't.
// Synchronously write it and get it over with.
@ -413,26 +403,26 @@ ChainDB.prototype.saveAsync = function saveAsync(entry, callback) {
err = e;
}
delete self._queue[entry.height];
delete self.queue[entry.height];
return callback(null, success);
});
};
ChainDB.prototype.drop = function drop(height) {
ChainDB.prototype._drop = function _drop(height) {
assert(height >= 0);
// Potential race condition here. Not sure how
// to handle this.
if (this._queue[height]) {
if (this.queue[height]) {
utils.debug('Warning: write job in progress.');
delete this._queue[height];
delete this.queue[height];
}
delete this._cache[height];
delete this.cache[height];
};
ChainDB.prototype.resetHeight = function resetHeight(height) {
ChainDB.prototype.resetHeightSync = function resetHeightSync(height) {
var size, count, existing;
if (typeof height === 'string')
@ -441,7 +431,7 @@ ChainDB.prototype.resetHeight = function resetHeight(height) {
assert(height >= 0);
size = (height + 1) * BLOCK_SIZE;
count = this.count();
count = this.getSize();
if (height === count - 1)
return;
@ -452,7 +442,7 @@ ChainDB.prototype.resetHeight = function resetHeight(height) {
for (i = height + 1; i < count; i++) {
existing = this.get(i);
assert(existing);
this.drop(i);
this._drop(i);
delete this.heightLookup[existing.hash];
}
@ -481,7 +471,7 @@ ChainDB.prototype.resetHeightAsync = function resetHeightAsync(height, callback)
callback = utils.asyncify(callback);
size = (height + 1) * BLOCK_SIZE;
count = this.count() - 1;
count = this.getSize();
pending = count - (height + 1);
if (height === count - 1)
@ -499,7 +489,7 @@ ChainDB.prototype.resetHeightAsync = function resetHeightAsync(height, callback)
return done(err);
assert(existing);
self.drop(i);
self._drop(i);
delete self.heightLookup[existing.hash];
if (!--pending)
done();
@ -604,7 +594,7 @@ ChainDB.prototype._readAsync = function _readAsync(size, offset, callback) {
}
if (!bytes)
throw new Error('_readAsync() failed.');
return callback(new Error('_readAsync() failed.'));
index += bytes;
size -= bytes;
@ -679,7 +669,7 @@ ChainDB.prototype._writeAsync = function _writeAsync(data, offset, callback) {
}
if (!bytes)
throw new Error('_writeAsync() failed.');
return callback(new Error('_writeAsync() failed.'));
index += bytes;
size -= bytes;

View File

@ -129,7 +129,7 @@ Miner.prototype.addBlock = function addBlock(block) {
// us, start over with the new target.
if (block.height > this.last.height) {
this.last = block.type === 'block'
? this.chain.getBlock(block)
? this.chain.getEntry(block)
: block;
assert(this.last);
this.start();
@ -304,7 +304,7 @@ Miner.prototype.iterate = function iterate() {
self.emit('block', self.block);
// Try to find a new block
self.last = self.chain.getBlock(self.block);
self.last = self.chain.getEntry(self.block);
assert(self.last);
self.block = self.createBlock();

View File

@ -1465,6 +1465,9 @@ Pool.prototype._request = function _request(peer, type, hash, options, cb) {
item = new LoadRequest(this, peer, type, hash, cb);
if (options.noQueue)
return;
if (item.type === 'tx') {
if (peer._txQueue.length === 0) {
utils.nextTick(function() {