diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 82bb7625..73cb36d9 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -33,6 +33,80 @@ var BufferReader = require('./reader'); var Framer = bcoin.protocol.framer; var Parser = bcoin.protocol.parser; +var layout = { + R: function R() { + var p = new BufferWriter(); + p.writeString('R', 'ascii'); + return p.render(); + }, + e: function e(hash) { + var p = new BufferWriter(); + p.writeString('e', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + h: function h(hash) { + var p = new BufferWriter(); + p.writeString('h', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + H: function H(height) { + var p = new BufferWriter(); + p.writeString('H', 'ascii'); + p.writeU32(height); + return p.render(); + }, + n: function n(hash) { + var p = new BufferWriter(); + p.writeString('n', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + b: function b(hash) { + var p = new BufferWriter(); + p.writeString('b', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + t: function t(hash) { + var p = new BufferWriter(); + p.writeString('t', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + c: function c(hash) { + var p = new BufferWriter(); + p.writeString('c', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + u: function u(hash) { + var p = new BufferWriter(); + p.writeString('u', 'ascii'); + p.writeHash(hash); + return p.render(); + }, + T: function T(address, hash) { + var p = new BufferWriter(); + p.writeString('T', 'ascii'); + p.writeString(address, 'hex'); + p.writeString('\0', 'binary'); + p.writeHash(hash); + return p.render(); + }, + C: function C(address, hash, index) { + var p = new BufferWriter(); + p.writeString('C', 'ascii'); + p.writeString(address, 'hex'); + p.writeString('\0', 'binary'); + p.writeHash(hash); + p.writeString('\0', 'binary'); + p.writeU32(index); + return p.render(); + } +}; + /** * The database backend for the {@link Chain} object. * @exports ChainDB @@ -138,7 +212,7 @@ ChainDB.prototype._init = function _init() { self.emit('open'); } - self.db.has('h/' + self.network.genesis.hash, function(err, exists) { + self.db.has(layout.h(self.network.genesis.hash), function(err, exists) { if (err) return self.emit('error', err); @@ -258,7 +332,7 @@ ChainDB.prototype.getHeight = function getHeight(hash, callback) { return callback(null, this.cacheHash.get(hash).height); } - this.db.fetch('h/' + hash, function(data) { + this.db.fetch(layout.h(hash), function(data) { assert(data.length === 4, 'Database corruption.'); return data.readUInt32LE(0, true); }, function(err, height) { @@ -295,7 +369,7 @@ ChainDB.prototype.getHash = function getHash(height, callback) { return callback(null, this.cacheHeight.get(height).hash); } - this.db.fetch('H/' + pad32(height), function(data) { + this.db.fetch(layout.H(height), function(data) { assert(data.length === 32, 'Database corruption.'); return data.toString('hex'); }, callback); @@ -380,7 +454,7 @@ ChainDB.prototype.getEntry = function getEntry(hash, callback) { if (self.cacheHash.has(hash)) return callback(null, self.cacheHash.get(hash)); - return self.db.fetch('e/' + hash, function(data) { + return self.db.fetch(layout.e(hash), function(data) { return bcoin.chainentry.fromRaw(self.chain, data); }, callback); }); @@ -437,8 +511,8 @@ ChainDB.prototype.save = function save(entry, block, view, connect, callback) { height = new Buffer(4); height.writeUInt32LE(height, 0, true); - batch.put('h/' + entry.hash, height); - batch.put('e/' + entry.hash, entry.toRaw()); + batch.put(layout.h(entry.hash), height); + batch.put(layout.e(entry.hash), entry.toRaw()); this.cacheHash.set(entry.hash, entry); @@ -452,9 +526,9 @@ ChainDB.prototype.save = function save(entry, block, view, connect, callback) { this.cacheHeight.set(entry.height, entry); - batch.put('n/' + entry.prevBlock, hash); - batch.put('H/' + pad32(entry.height), hash); - batch.put('R', hash); + batch.put(layout.n(entry.prevBlock), hash); + batch.put(layout.H(entry.height), hash); + batch.put(layout.R(), hash); this.emit('add entry', entry); @@ -472,7 +546,7 @@ ChainDB.prototype.save = function save(entry, block, view, connect, callback) { ChainDB.prototype.getTip = function getTip(callback) { var self = this; - return this.db.fetch('R', function(data) { + return this.db.fetch(layout.R(), function(data) { assert(data.length === 32, 'Database corruption.'); return data.toString('hex'); }, function(err, hash) { @@ -496,9 +570,9 @@ ChainDB.prototype.connect = function connect(entry, block, view, callback) { var batch = this.db.batch(); var hash = new Buffer(entry.hash, 'hex'); - batch.put('n/' + entry.prevBlock, hash); - batch.put('H/' + pad32(entry.height), hash); - batch.put('R', hash); + batch.put(layout.n(entry.prevBlock), hash); + batch.put(layout.H(entry.height), hash); + batch.put(layout.R(), hash); this.cacheHash.set(entry.hash, entry); this.cacheHeight.set(entry.height, entry); @@ -529,9 +603,9 @@ ChainDB.prototype.disconnect = function disconnect(block, callback) { batch = this.db.batch(); - batch.del('n/' + entry.prevBlock); - batch.del('H/' + pad32(entry.height)); - batch.put('R', new Buffer(entry.prevBlock, 'hex')); + batch.del(layout.n(entry.prevBlock)); + batch.del(layout.H(entry.height)); + batch.put(layout.R(), new Buffer(entry.prevBlock, 'hex')); this.cacheHeight.remove(entry.height); @@ -564,7 +638,7 @@ ChainDB.prototype.disconnect = function disconnect(block, callback) { */ ChainDB.prototype.getNextHash = function getNextHash(hash, callback) { - return this.db.fetch('n/' + hash, function(data) { + return this.db.fetch(layout.n(hash), function(data) { assert(data.length === 32, 'Database corruption.'); return data.toString('hex'); }, callback); @@ -641,14 +715,14 @@ ChainDB.prototype.reset = function reset(block, callback) { batch = self.db.batch(); if (tip.hash === entry.hash) { - batch.put('R', new Buffer(tip.hash, 'hex')); + batch.put(layout.R(), new Buffer(tip.hash, 'hex')); return batch.write(callback); } - batch.del('H/' + pad32(tip.height)); - batch.del('h/' + tip.hash); - batch.del('e/' + tip.hash); - batch.del('n/' + tip.prevBlock); + batch.del(layout.H(tip.height)); + batch.del(layout.h(tip.hash)); + batch.del(layout.e(tip.hash)); + batch.del(layout.n(tip.prevBlock)); self.emit('remove entry', tip); @@ -699,7 +773,7 @@ ChainDB.prototype.saveBlock = function saveBlock(block, view, batch, connect, ca if (this.options.spv) return utils.nextTick(callback); - batch.put('b/' + block.hash('hex'), block.render()); + batch.put(layout.b(block.hash()), block.render()); if (!connect) return utils.nextTick(callback); @@ -725,7 +799,7 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { if (!block) return callback(); - batch.del('b/' + block.hash('hex')); + batch.del(layout.b(block.hash())); if (self.options.spv) return callback(null, block); @@ -760,12 +834,12 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, batch, callb hash = tx.hash('hex'); if (this.options.indexTX) { - batch.put('t/' + hash, tx.toExtended()); + batch.put(layout.t(hash), tx.toExtended()); if (this.options.indexAddress) { addresses = tx.getHashes(); for (j = 0; j < addresses.length; j++) { address = addresses[j]; - batch.put('T/' + address + '/' + hash, DUMMY); + batch.put(layout.T(address, hash), DUMMY); } } } @@ -782,7 +856,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, batch, callb if (this.options.indexAddress) { address = input.getHash(); if (address) - batch.del('C/' + address + '/' + key); + batch.del(layout.C(address, input.prevout.hash, input.prevout.index)); } Framer.coin(input.coin, false, undo); @@ -797,7 +871,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, batch, callb if (this.options.indexAddress) { address = output.getHash(); if (address) - batch.put('C/' + address + '/' + key, DUMMY); + batch.put(layout.C(address, hash, j), DUMMY); } } } @@ -807,17 +881,17 @@ ChainDB.prototype.connectBlock = function connectBlock(block, view, batch, callb for (i = 0; i < view.length; i++) { coins = view[i]; if (coins.count() === 0) { - batch.del('c/' + coins.hash); + batch.del(layout.c(coins.hash)); this.coinCache.remove(coins.hash); } else { raw = coins.toRaw(); - batch.put('c/' + coins.hash, raw); + batch.put(layout.c(coins.hash), raw); this.coinCache.set(coins.hash, raw); } } if (undo.written > 0) - batch.put('u/' + block.hash('hex'), undo.render()); + batch.put(layout.u(block.hash()), undo.render()); this.emit('add block', block); @@ -851,12 +925,12 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb hash = tx.hash('hex'); if (self.options.indexTX) { - batch.del('t/' + hash); + batch.del(layout.t(hash)); if (self.options.indexAddress) { addresses = tx.getHashes(); for (j = 0; j < addresses.length; j++) { address = addresses[j]; - batch.del('T/' + address + '/' + hash); + batch.del(layout.T(address, hash)); } } } @@ -873,7 +947,7 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb if (self.options.indexAddress) { address = input.getHash(); if (address) - batch.put('C/' + address + '/' + key, DUMMY); + batch.put(layout.C(address, input.prevout.hash, input.prevout.index), DUMMY); } } @@ -889,7 +963,7 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb if (self.options.indexAddress) { address = output.getHash(); if (address) - batch.del('C/' + address + '/' + key); + batch.del(layout.C(address, hash, j)); } view.spend(hash, j); @@ -901,16 +975,16 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb for (i = 0; i < view.length; i++) { coins = view[i]; if (coins.count() === 0) { - batch.del('c/' + coins.hash); + batch.del(layout.c(coins.hash)); self.coinCache.remove(coins.hash); } else { raw = coins.toRaw(); - batch.put('c/' + coins.hash, raw); + batch.put(layout.c(coins.hash), raw); self.coinCache.set(coins.hash, raw); } } - batch.del('u/' + block.hash('hex')); + batch.del(layout.u(block.hash())); self.emit('remove block', block); @@ -1036,7 +1110,7 @@ ChainDB.prototype.getCoins = function getCoins(hash, callback) { return callback(null, coins); } - this.db.fetch('c/' + hash, function(data) { + this.db.fetch(layout.c(hash), function(data) { self.coinCache.set(hash, data); return bcoin.coins.fromRaw(data, hash); }, callback); @@ -1054,7 +1128,7 @@ ChainDB.prototype.getTX = function getTX(hash, callback) { if (!this.options.indexTX) return utils.nextTick(callback); - this.db.fetch('t/' + hash, function(data) { + this.db.fetch(layout.t(hash), function(data) { return bcoin.tx.fromExtended(data); }, function(err, tx) { if (err) @@ -1076,7 +1150,7 @@ ChainDB.prototype.hasTX = function hasTX(hash, callback) { if (!this.options.indexTX) return utils.asyncify(callback)(null, false); - return this.db.has('t/' + hash, callback); + return this.db.has(layout.t(hash), callback); }; /** @@ -1266,7 +1340,7 @@ ChainDB.prototype.getCoinView = function getCoinView(block, callback) { */ ChainDB.prototype.getUndoCoins = function getUndoCoins(hash, callback) { - return this.db.fetch('u/' + hash, function(data) { + return this.db.fetch(layout.u(hash), function(data) { var p = new BufferReader(data); var coins = []; var coin; @@ -1337,7 +1411,7 @@ ChainDB.prototype.getBlock = function getBlock(hash, callback) { if (!hash) return callback(); - self.db.fetch('b/' + hash, function(data) { + self.db.fetch(layout.b(hash), function(data) { var block = bcoin.block.fromRaw(data); block.setHeight(height); return block; @@ -1411,8 +1485,8 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { return callback(); batch.del(key); - batch.del('b/' + hash); - batch.del('u/' + hash); + batch.del(layout.b(hash)); + batch.del(layout.u(hash)); return callback(); });