diff --git a/lib/chain/chain.js b/lib/chain/chain.js index 66699f06..18ed4757 100644 --- a/lib/chain/chain.js +++ b/lib/chain/chain.js @@ -903,7 +903,7 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) { } // Save block and connect inputs. - yield this.db.save(entry, block, view, true); + yield this.db.save(entry, block, view); this.tip = entry; this.height = entry.height; @@ -1186,7 +1186,7 @@ Chain.prototype._add = co(function* add(block) { // our tip's. Add the block but do _not_ // connect the inputs. if (entry.chainwork.cmp(this.tip.chainwork) <= 0) { - yield this.db.save(entry, block, null, false); + yield this.db.save(entry, block); this.emit('competitor', block, entry); diff --git a/lib/chain/chaindb.js b/lib/chain/chaindb.js index d703cab7..fa6447ec 100644 --- a/lib/chain/chaindb.js +++ b/lib/chain/chaindb.js @@ -16,6 +16,8 @@ var DUMMY = new Buffer([0]); var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); var spawn = require('../utils/spawn'); +var CoinView = require('./coinview'); +var Coins = require('./coins'); var co = spawn.co; /* @@ -232,7 +234,7 @@ ChainDB.prototype._open = co(function* open() { genesis = bcoin.chainentry.fromBlock(this.chain, block); - yield this.save(genesis, block, null, true); + yield this.save(genesis, block, new CoinView()); } this.logger.info('Chain successfully loaded.'); @@ -544,28 +546,26 @@ ChainDB.prototype.get = co(function* get(hash) { * instead performed in {@link Chain#add}. * @param {ChainEntry} entry * @param {Block} block - * @param {CoinView} view - * @param {Boolean} connect - Whether to connect the - * block's inputs and add it as a tip. + * @param {CoinView?} view - Will not connect if null. * @returns {Promise} */ -ChainDB.prototype.save = co(function* save(entry, block, view, connect) { +ChainDB.prototype.save = co(function* save(entry, block, view) { var hash = block.hash(); var height = new Buffer(4); - this.start(); - height.writeUInt32LE(entry.height, 0, true); + this.start(); + this.put(layout.h(hash), height); this.put(layout.e(hash), entry.toRaw()); this.cacheHash.set(entry.hash, entry); - if (!connect) { + if (!view) { try { - yield this.saveBlock(block, view, false); + yield this.saveBlock(block); } catch (e) { this.drop(); throw e; @@ -579,13 +579,14 @@ ChainDB.prototype.save = co(function* save(entry, block, view, connect) { this.put(layout.H(entry.height), hash); try { - yield this.saveBlock(block, view, true); + yield this.saveBlock(block, view); } catch (e) { this.drop(); throw e; } this.put(layout.R, this.pending.commit(hash)); + yield this.commit(); }); @@ -814,13 +815,13 @@ ChainDB.prototype.has = co(function* has(height) { * @returns {Promise} - Returns {@link Block}. */ -ChainDB.prototype.saveBlock = co(function* saveBlock(block, view, connect) { +ChainDB.prototype.saveBlock = co(function* saveBlock(block, view) { if (this.options.spv) return block; this.put(layout.b(block.hash()), block.toRaw()); - if (!connect) + if (!view) return block; yield this.connectBlock(block, view); @@ -1107,7 +1108,7 @@ ChainDB.prototype.getCoin = co(function* getCoin(hash, index) { var coins = this.coinCache.get(hash); if (coins) - return bcoin.coins.parseCoin(coins, hash, index); + return Coins.parseCoin(coins, hash, index); coins = yield this.db.get(layout.c(hash)); @@ -1116,7 +1117,7 @@ ChainDB.prototype.getCoin = co(function* getCoin(hash, index) { this.coinCache.set(hash, coins); - return bcoin.coins.parseCoin(coins, hash, index); + return Coins.parseCoin(coins, hash, index); }); /** @@ -1129,7 +1130,7 @@ ChainDB.prototype.getCoins = co(function* getCoins(hash) { var coins = this.coinCache.get(hash); if (coins) - return bcoin.coins.fromRaw(coins, hash); + return Coins.fromRaw(coins, hash); coins = yield this.db.get(layout.c(hash)); @@ -1138,7 +1139,7 @@ ChainDB.prototype.getCoins = co(function* getCoins(hash) { this.coinCache.set(hash, coins); - return bcoin.coins.fromRaw(coins, hash); + return Coins.fromRaw(coins, hash); }); /** @@ -1401,7 +1402,7 @@ ChainDB.prototype.getFullBlock = co(function* getFullBlock(hash) { */ ChainDB.prototype.getCoinView = co(function* getCoinView(block, callback) { - var view = new bcoin.coinview(); + var view = new CoinView(); var prevout = block.getPrevout(); var i, prev, coins; diff --git a/lib/db/lowlevelup.js b/lib/db/lowlevelup.js index ca200a04..fd8e78d3 100644 --- a/lib/db/lowlevelup.js +++ b/lib/db/lowlevelup.js @@ -232,7 +232,8 @@ LowlevelUp.prototype.iterator = function iterator(options) { fillCache: options.fillCache || false, keyAsBuffer: this.bufferKeys, valueAsBuffer: true, - reverse: options.reverse || false + reverse: options.reverse || false, + highWaterMark: options.highWaterMark || 16 * 1024 }; // Workaround for a leveldown @@ -303,19 +304,24 @@ LowlevelUp.prototype.has = co(function* has(key) { LowlevelUp.prototype.iterate = co(function* iterate(options) { var items = []; var parse = options.parse; - var iter, result, data; + var iter, item, data; assert(typeof parse === 'function', 'Parse must be a function.'); iter = this.iterator(options); for (;;) { - result = yield iter.next(); + item = yield iter.next(); - if (!result) + if (!item) break; - data = parse(result.key, result.value); + try { + data = parse(item.key, item.value); + } catch (e) { + yield iter.end(); + throw e; + } if (data) items.push(data); @@ -357,7 +363,7 @@ LowlevelUp.prototype.clone = co(function* clone(path) { var opt = { keys: true, values: true }; var hwm = 256 << 20; var total = 0; - var tmp, batch, iter, result; + var tmp, batch, iter, item; assert(!this.loading); assert(!this.closing); @@ -374,12 +380,12 @@ LowlevelUp.prototype.clone = co(function* clone(path) { iter = this.iterator(opt); for (;;) { - result = yield iter.next(); + item = yield iter.next(); - if (!result) + if (!item) break; - batch.put(result.key, result.value); + batch.put(item.key, item.value); total += value.length; if (total >= hwm) { @@ -387,6 +393,7 @@ LowlevelUp.prototype.clone = co(function* clone(path) { try { yield batch.write(); } catch (e) { + yield iter.end(); yield tmp.close(); throw e; } diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 11ba3d95..c78afa08 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -576,8 +576,7 @@ Mempool.prototype.addTX = co(function* addTX(tx) { Mempool.prototype._addTX = co(function* _addTX(tx) { var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; var hash = tx.hash('hex'); - var ret, entry, missing; - var result, exists; + var ret, entry, result, exists; assert(!tx.mutable, 'Cannot add mutable TX to mempool.'); @@ -665,10 +664,8 @@ Mempool.prototype._addTX = co(function* _addTX(tx) { yield this.fillAllCoins(tx); - if (!tx.hasCoins()) { - missing = this.storeOrphan(tx); - return missing; - } + if (!tx.hasCoins()) + return this.storeOrphan(tx); entry = MempoolEntry.fromTX(tx, this.chain.height); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index c6a00f49..f4bc963f 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -239,7 +239,7 @@ WalletDB.prototype.backup = function backup(path) { */ WalletDB.prototype.getDepth = co(function* getDepth() { - var result, iter, depth; + var iter, item, depth; // This may seem like a strange way to do // this, but updating a global state when @@ -256,14 +256,14 @@ WalletDB.prototype.getDepth = co(function* getDepth() { reverse: true }); - result = yield iter.next(); + item = yield iter.next(); - if (!result) + if (!item) return 1; yield iter.end(); - depth = layout.ww(result.key); + depth = layout.ww(item.key); return depth + 1; }); diff --git a/migrate/chaindb0to1.js b/migrate/chaindb0to1.js index 8d5dd384..7c8ac41b 100644 --- a/migrate/chaindb0to1.js +++ b/migrate/chaindb0to1.js @@ -1,4 +1,6 @@ var bcoin = require('../'); +var spawn = bcoin.spawn; +var co = spawn.co; var assert = require('assert'); var file = process.argv[2]; @@ -22,91 +24,93 @@ function makeKey(data) { return key; } -function updateState(callback) { - var hash, batch, ver, p; +var checkVersion = co(function* checkVersion() { + var data, ver; + + console.log('Checking version.'); + + data = yield db.get('V'); + + if (!data) + return; + + ver = data.readUInt32LE(0, true); + + if (ver !== 0) + throw Error('DB is version ' + ver + '.'); +}); + +var updateState = co(function* updateState() { + var data, hash, batch, ver, p; console.log('Updating chain state.'); - db.get('R', function(err, data) { - if (err) - return callback(err); + data = yield db.get('R'); - if (!data || data.length < 32) - return callback(new Error('No chain state.')); + if (!data || data.length < 32) + throw new Error('No chain state.'); - hash = data.slice(0, 32); + hash = data.slice(0, 32); - p = new bcoin.writer(); - p.writeHash(hash); - p.writeU64(0); - p.writeU64(0); - p.writeU64(0); - p = p.render(); + p = new bcoin.writer(); + p.writeHash(hash); + p.writeU64(0); + p.writeU64(0); + p.writeU64(0); + p = p.render(); - batch = db.batch(); + batch = db.batch(); - batch.put('R', p); + batch.put('R', p); - ver = new Buffer(4); - ver.writeUInt32LE(1, 0, true); - batch.put('V', ver); + ver = new Buffer(4); + ver.writeUInt32LE(1, 0, true); + batch.put('V', ver); - batch.write(function(err) { - if (err) - return callback(err); - console.log('Updated chain state.'); - callback(); - }); - }); -} + yield batch.write(); -function updateEndian(callback) { - var lo = new Buffer('4800000000', 'hex'); - var hi = new Buffer('48ffffffff', 'hex'); + console.log('Updated chain state.'); +}); + +var updateEndian = co(function* updateEndian() { var batch = db.batch(); var total = 0; + var iter, item; console.log('Updating endianness.'); console.log('Iterating...'); - db.iterate({ - gte: lo, - lte: hi, - values: true, - parse: function(key, value) { - batch.del(key); - batch.put(makeKey(key), value); - total++; - } - }, function(err) { - if (err) - throw err; - - console.log('Migrating %d items.', total); - - batch.write(function(err) { - if (err) - throw err; - console.log('Migrated endianness.'); - callback(); - }); + iter = db.iterator({ + gte: new Buffer('4800000000', 'hex'), + lte: new Buffer('48ffffffff', 'hex'), + values: true }); -} -db.open(function(err) { - if (err) - throw err; + for (;;) { + item = yield iter.next(); - console.log('Opened %s.', file); + if (!item) + break; - updateState(function(err) { - if (err) - throw err; - updateEndian(function(err) { - if (err) - throw err; - console.log('Migration complete.'); - process.exit(0); - }); - }); + batch.del(item.key); + batch.put(makeKey(item.key), item.value); + total++; + } + + console.log('Migrating %d items.', total); + + yield batch.write(); + + console.log('Migrated endianness.'); +}); + +spawn(function *() { + yield db.open(); + console.log('Opened %s.', file); + yield checkVersion(); + yield updateState(); + yield updateEndian(); +}).then(function() { + console.log('Migration complete.'); + process.exit(0); });