diff --git a/bin/cli b/bin/cli index 598d83ff..167a3752 100755 --- a/bin/cli +++ b/bin/cli @@ -323,12 +323,6 @@ CLI.prototype.resendWallet = co(function* resendWallet() { this.log('Resending...'); }); -CLI.prototype.zap = co(function* zap() { - var age = +this.argv[0] || 72 * 3600; - yield this.client.zap(age); - this.log('Zapped.'); -}); - CLI.prototype.backup = co(function* backup() { var path = this.argv[0]; @@ -529,8 +523,6 @@ CLI.prototype.handleNode = co(function* handleNode() { return yield this.rescan(); case 'resend': return yield this.resend(); - case 'zap': - return yield this.zap(); case 'backup': return yield this.backup(); case 'rpc': @@ -547,7 +539,6 @@ CLI.prototype.handleNode = co(function* handleNode() { this.log(' $ block [hash/height]: View block.'); this.log(' $ rescan [height/hash]: Rescan for transactions.'); this.log(' $ resend: Resend pending transactions.'); - this.log(' $ zap [age?]: Zap stale transactions from walletdb.'); this.log(' $ backup [path]: Backup the wallet db.'); this.log(' $ rpc [command] [args]: Execute RPC command.'); return; diff --git a/lib/http/client.js b/lib/http/client.js index 0294b6b3..3d5fc6f4 100644 --- a/lib/http/client.js +++ b/lib/http/client.js @@ -362,17 +362,6 @@ HTTPClient.prototype.resend = function resend() { return this._post('/resend', {}); }; -/** - * Zap stale transactions. - * @param {Number} age - * @returns {Promise} - */ - -HTTPClient.prototype.zap = function zap(age) { - var options = { age: age }; - return this._post('/zap', options); -}; - /** * Backup the walletdb. * @param {String} path diff --git a/lib/http/server.js b/lib/http/server.js index bbe17cfd..50023a5e 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -740,18 +740,6 @@ HTTPServer.prototype._init = function _init() { send(200, { success: true }); })); - // Zap - this.post('/zap', con(function* (req, res, send, next) { - var options = req.options; - var age = options.age; - - enforce(age != null, 'Age is required.'); - - yield this.walletdb.zap(age); - - send(200, { success: true }); - })); - // Backup WalletDB this.post('/backup', con(function* (req, res, send, next) { var options = req.options; diff --git a/lib/wallet/browser.js b/lib/wallet/browser.js index 5d814448..0bc9cd8b 100644 --- a/lib/wallet/browser.js +++ b/lib/wallet/browser.js @@ -45,11 +45,8 @@ layout.walletdb = { return [+key.slice(1, 11), key.slice(11)]; }, R: 'R', - c: function c(height) { - return 'c' + pad32(height); - }, - cc: function cc(key) { - return +key.slice(1); + h: function c(height) { + return 'h' + pad32(height); }, b: function b(height) { return 'b' + pad32(height); @@ -57,12 +54,6 @@ layout.walletdb = { bb: function bb(key) { return +key.slice(1); }, - e: function e(hash) { - return 'e' + hash; - }, - ee: function ee(key) { - return key.slice(1); - }, o: function o(hash) { return 'o' + hash + pad32(index); }, diff --git a/lib/wallet/records.js b/lib/wallet/records.js index 0b6e3d36..28494c6f 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -362,19 +362,6 @@ TXMapRecord.prototype.remove = function remove(wid) { return utils.binaryRemove(this.wids, wid, cmp); }; -TXMapRecord.prototype.toRaw = function toRaw() { - return serializeWallets(this.wids); -}; - -TXMapRecord.prototype.fromRaw = function fromRaw(data) { - this.wids = parseWallets(data); - return this; -}; - -TXMapRecord.fromRaw = function fromRaw(hash, data) { - return new TXMapRecord(hash).fromRaw(data); -}; - /** * Outpoint Map * @constructor diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index c315d983..5d17e702 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -19,7 +19,6 @@ var Coin = require('../primitives/coin'); var Outpoint = require('../primitives/outpoint'); var records = require('./records'); var BlockMapRecord = records.BlockMapRecord; -var TXMapRecord = records.TXMapRecord; var OutpointMapRecord = records.OutpointMapRecord; var DUMMY = new Buffer([0]); @@ -887,49 +886,6 @@ TXDB.prototype.isSpent = function isSpent(hash, index) { return this.has(layout.s(hash, index)); }; -/** - * Append to the global tx record. - * @param {TX} tx - * @returns {Promise} - */ - -TXDB.prototype.addTXMap = co(function* addTXMap(tx) { - var hash = tx.hash('hex'); - var map = yield this.walletdb.getTXMap(hash); - - if (!map) - map = new TXMapRecord(hash); - - if (!map.add(this.wallet.wid)) - return; - - this.walletdb.writeTXMap(this.wallet, hash, map); -}); - -/** - * Remove from the global tx record. - * @param {TX} tx - * @returns {Promise} - */ - -TXDB.prototype.removeTXMap = co(function* removeTXMap(tx) { - var hash = tx.hash('hex'); - var map = yield this.walletdb.getTXMap(hash); - - if (!map) - return; - - if (!map.remove(this.wallet.wid)) - return; - - if (map.wids.length === 0) { - this.walletdb.unwriteTXMap(this.wallet, hash); - return; - } - - this.walletdb.writeTXMap(this.wallet, hash, map); -}); - /** * Append to the global unspent record. * @param {Hash} hash @@ -1311,8 +1267,6 @@ TXDB.prototype.insert = co(function* insert(tx, block) { this.put(layout.H(account, tx.height, hash), DUMMY); } - yield this.addTXMap(tx); - if (tx.height !== -1) yield this.addBlockMap(tx, tx.height); @@ -1483,8 +1437,6 @@ TXDB.prototype._confirm = co(function* confirm(tx, block) { this.put(layout.H(account, tx.height, hash), DUMMY); } - yield this.addTXMap(tx); - if (tx.height !== -1) yield this.addBlockMap(tx, tx.height); @@ -1615,8 +1567,6 @@ TXDB.prototype.erase = co(function* erase(tx) { this.del(layout.H(account, tx.height, hash)); } - yield this.removeTXMap(tx); - if (tx.height !== -1) yield this.removeBlockMap(tx, tx.height); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 40e1c813..ed240ebc 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -29,7 +29,6 @@ var ChainState = records.ChainState; var BlockMapRecord = records.BlockMapRecord; var BlockMeta = records.BlockMeta; var PathMapRecord = records.PathMapRecord; -var TXMapRecord = records.TXMapRecord; var OutpointMapRecord = records.OutpointMapRecord; var TXDB = require('./txdb'); var U32 = utils.U32; @@ -43,10 +42,10 @@ var U32 = utils.U32; * a[wid][index] -> account * i[wid][name] -> account index * t[wid]* -> txdb - * R -> tip height - * c[height] -> chain header + * R -> chain sync state + * h[height] -> recent block hash * b[height] -> block->wid map - * e[hash] -> tx->wid map + * o[hash][index] -> outpoint->wid map */ var layout = { @@ -109,15 +108,12 @@ var layout = { return [key.readUInt32BE(1, true), key.toString('ascii', 5)]; }, R: new Buffer([0x52]), - c: function c(height) { + h: function h(height) { var key = new Buffer(5); - key[0] = 0x63; + key[0] = 0x68; key.writeUInt32BE(height, 1, true); return key; }, - cc: function cc(key) { - return key.readUInt32BE(1, true); - }, b: function b(height) { var key = new Buffer(5); key[0] = 0x62; @@ -127,18 +123,9 @@ var layout = { bb: function bb(key) { return key.readUInt32BE(1, true); }, - e: function e(hash) { - var key = new Buffer(33); - key[0] = 0x65; - key.write(hash, 1, 'hex'); - return key; - }, - ee: function ee(key) { - return key.toString('hex', 1); - }, o: function o(hash, index) { var key = new Buffer(37); - key[0] = 0x01; + key[0] = 0x6f; key.write(hash, 1, 'hex'); key.writeUInt32BE(index, 33, true); return key; @@ -266,17 +253,30 @@ WalletDB.prototype._close = co(function* close() { }); /** - * Connect and sync with the chain server (without a lock). + * Watch addresses and outpoints. * @private * @returns {Promise} */ WalletDB.prototype.watch = co(function* watch() { - var data = yield this.getFilterData(); + var hashes = yield this.getHashes(); + var outpoints = yield this.getOutpoints(); + var chunks = []; + var i, hash, outpoint; - this.logger.info('Adding %d hashes to WalletDB filter.', data.length); + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + chunks.push(hash); + } - this.addFilter(data); + for (i = 0; i < outpoints.length; i++) { + outpoint = outpoints[i]; + chunks.push(outpoint.toRaw()); + } + + this.logger.info('Adding %d chunks to WalletDB filter.', chunks.length); + + this.addFilter(chunks); }); /** @@ -460,6 +460,7 @@ WalletDB.prototype.wipe = co(function* wipe() { this.logger.warning('Wiping WalletDB TXDB...'); this.logger.warning('I hope you know what you\'re doing.'); + // All TXDB records keys = yield this.db.keys({ gte: TXDB.layout.prefix(0x00000000, dummy), lte: TXDB.layout.prefix(0xffffffff, dummy) @@ -470,6 +471,62 @@ WalletDB.prototype.wipe = co(function* wipe() { batch.del(key); } + // All recent block hashes + keys = yield this.db.keys({ + gte: layout.h(0), + lte: layout.h(0xffffffff) + }); + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + batch.del(key); + } + + // The state record + batch.del(layout.R); + + // All block maps + keys = yield this.db.keys({ + gte: layout.b(0), + lte: layout.b(0xffffffff) + }); + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + batch.del(key); + } + + // All outpoint maps + keys = yield this.db.keys({ + gte: layout.o(constants.NULL_HASH, 0), + lte: layout.o(constants.HIGH_HASH, 0xffffffff) + }); + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + batch.del(key); + } + + // All TX maps (e -- old) + gte = new Buffer(33); + gte.fill(0); + gte[0] = 0x65; + + lte = new Buffer(33); + lte.fill(255); + lte[0] = 0x65; + + keys = yield this.db.keys({ + gte: gte, + lte: lte + }); + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + batch.del(key); + } + + // All block maps (b -- 32 byte old) gte = new Buffer(33); gte.fill(0); gte[0] = 0x62; @@ -488,9 +545,18 @@ WalletDB.prototype.wipe = co(function* wipe() { batch.del(key); } + // All block records (c -- old) + gte = new Buffer(33); + gte.fill(0); + gte[0] = 0x63; + + lte = new Buffer(33); + lte.fill(255); + lte[0] = 0x63; + keys = yield this.db.keys({ - gte: layout.b(0), - lte: layout.b(0xffffffff) + gte: gte, + lte: lte }); for (i = 0; i < keys.length; i++) { @@ -498,28 +564,6 @@ WalletDB.prototype.wipe = co(function* wipe() { batch.del(key); } - keys = yield this.db.keys({ - gte: layout.c(0), - lte: layout.c(0xffffffff) - }); - - for (i = 0; i < keys.length; i++) { - key = keys[i]; - batch.del(key); - } - - keys = yield this.db.keys({ - gte: layout.e(constants.NULL_HASH), - lte: layout.e(constants.HIGH_HASH) - }); - - for (i = 0; i < keys.length; i++) { - key = keys[i]; - batch.del(key); - } - - batch.del(layout.R); - yield batch.write(); }); @@ -1201,30 +1245,6 @@ WalletDB.prototype.getOutpoints = function getOutpoints() { }); }; -/** - * Get hashes required for rescan filter. - * @returns {Promise} - */ - -WalletDB.prototype.getFilterData = co(function* getFilterData() { - var chunks = []; - var hashes = yield this.getHashes(); - var unspent = yield this.getOutpoints(); - var i, hash, outpoint; - - for (i = 0; i < hashes.length; i++) { - hash = hashes[i]; - chunks.push(hash); - } - - for (i = 0; i < unspent.length; i++) { - outpoint = unspent[i]; - chunks.push(outpoint.toRaw()); - } - - return chunks; -}); - /** * Get all address hashes. * @param {WalletID} wid @@ -1358,7 +1378,7 @@ WalletDB.prototype.getPendingKeys = co(function* getPendingKeys() { var keys = []; var iter, item; - iter = yield this.db.iterator({ + iter = this.db.iterator({ gte: layout.prefix(0x00000000, dummy), lte: layout.prefix(0xffffffff, dummy) }); @@ -1407,34 +1427,6 @@ WalletDB.prototype.getPendingTX = co(function* getPendingTX() { return result; }); -/** - * Get all wallet IDs with pending txs in them. - * @returns {Promise} - */ - -WalletDB.prototype.getPendingWallets = co(function* getPendingWallets() { - var layout = TXDB.layout; - var keys = yield this.getPendingKeys(); - var uniq = {}; - var result = []; - var i, key, wid; - - for (i = 0; i < keys.length; i++) { - key = keys[i]; - - wid = layout.pre(key); - - if (uniq[wid]) - continue; - - uniq[wid] = true; - - result.push(wid); - } - - return result; -}); - /** * Resend all pending transactions. * @returns {Promise} @@ -1602,8 +1594,8 @@ WalletDB.prototype.resetState = co(function* resetState(tip) { var iter, item; iter = this.db.iterator({ - gte: layout.c(0), - lte: layout.c(0xffffffff), + gte: layout.h(0), + lte: layout.h(0xffffffff), values: false }); @@ -1625,7 +1617,7 @@ WalletDB.prototype.resetState = co(function* resetState(tip) { state.startHash = tip.hash; state.height = tip.height; - batch.put(layout.c(tip.height), tip.toHash()); + batch.put(layout.h(tip.height), tip.toHash()); batch.put(layout.R, state.toRaw()); yield batch.write(); @@ -1654,7 +1646,7 @@ WalletDB.prototype.syncState = co(function* syncState(tip) { blocks = this.keepBlocks; for (i = 0; i < blocks; i++) { - batch.del(layout.c(height)); + batch.del(layout.h(height)); height--; } } else if (tip.height > state.height) { @@ -1664,13 +1656,13 @@ WalletDB.prototype.syncState = co(function* syncState(tip) { height = tip.height - this.keepBlocks; if (height >= 0) - batch.del(layout.c(height)); + batch.del(layout.h(height)); } state.height = tip.height; // Save tip and state. - batch.put(layout.c(tip.height), tip.toHash()); + batch.put(layout.h(tip.height), tip.toHash()); batch.put(layout.R, state.toRaw()); yield batch.write(); @@ -1702,32 +1694,6 @@ WalletDB.prototype.unwriteBlockMap = function unwriteBlockMap(wallet, height) { batch.del(layout.b(height)); }; -/** - * Add a transaction to global tx map. - * @param {Wallet} wallet - * @param {Hash} hash - * @param {TXMapRecord} map - * @returns {Promise} - */ - -WalletDB.prototype.writeTXMap = function writeTXMap(wallet, hash, map) { - var batch = this.batch(wallet); - batch.put(layout.e(hash), map.toRaw()); - this.addFilter(hash); -}; - -/** - * Remove a transaction from global tx map. - * @param {Wallet} wallet - * @param {Hash} hash - * @returns {Promise} - */ - -WalletDB.prototype.unwriteTXMap = function unwriteTXMap(wallet, hash) { - var batch = this.batch(wallet); - batch.del(layout.e(hash)); -}; - /** * Add an outpoint to global unspent map. * @param {Wallet} wallet @@ -1739,8 +1705,10 @@ WalletDB.prototype.unwriteTXMap = function unwriteTXMap(wallet, hash) { WalletDB.prototype.writeOutpointMap = function writeOutpointMap(wallet, hash, i, map) { var batch = this.batch(wallet); - batch.put(layout.o(hash, i), map.toRaw()); + this.addFilter(new Outpoint(hash, i).toRaw()); + + batch.put(layout.o(hash, i), map.toRaw()); }; /** @@ -1778,7 +1746,7 @@ WalletDB.prototype.getBlockMap = co(function* getBlockMap(height) { */ WalletDB.prototype.getBlock = co(function* getBlock(height) { - var data = yield this.db.get(layout.c(height)); + var data = yield this.db.get(layout.h(height)); var block; if (!data) @@ -1806,21 +1774,6 @@ WalletDB.prototype.getTip = co(function* getTip() { return tip; }); -/** - * Get a TX->Wallet map. - * @param {Hash} hash - * @returns {Promise} - */ - -WalletDB.prototype.getTXMap = co(function* getTXMap(hash) { - var data = yield this.db.get(layout.e(hash)); - - if (!data) - return; - - return TXMapRecord.fromRaw(hash, data); -}); - /** * Get a Unspent->Wallet map. * @param {Hash} hash @@ -1889,12 +1842,13 @@ WalletDB.prototype.rollback = co(function* rollback(height) { WalletDB.prototype.revert = co(function* revert(height) { var total = 0; - var i, iter, item, block, tx; + var i, iter, item, height, block, tx; iter = this.db.iterator({ gte: layout.b(height + 1), lte: layout.b(0xffffffff), - reverse: true + reverse: true, + values: true }); for (;;) { @@ -1904,7 +1858,8 @@ WalletDB.prototype.revert = co(function* revert(height) { break; try { - block = BlockMapRecord.fromRaw(item.value); + height = layout.bb(item.key); + block = BlockMapRecord.fromRaw(height, item.value); total += block.txs.length; for (i = block.txs.length - 1; i >= 0; i--) { @@ -2143,40 +2098,6 @@ WalletDB.prototype._unconfirm = co(function* unconfirm(tx) { } }); -/** - * Zap stale transactions. - * @param {Number} age - * @returns {Promise} - */ - -WalletDB.prototype.zap = co(function* zap(age) { - var unlock = yield this.txLock.lock(); - try { - return yield this._zap(age); - } finally { - unlock(); - } -}); - -/** - * Zap stale transactions without a lock. - * @private - * @param {Number} age - * @returns {Promise} - */ - -WalletDB.prototype._zap = co(function* zap(age) { - var wids = yield this.getPendingWallets(); - var i, wid, wallet; - - for (i = 0; i < wids.length; i++) { - wid = wids[i]; - wallet = yield this.get(wid); - assert(wallet); - yield wallet.zap(age); - } -}); - /* * Helpers */