From aed03c265538cf929d08526d80a3a6a3cb24fbf6 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 10 Oct 2016 07:13:42 -0700 Subject: [PATCH] txdb: add txdb state. --- lib/http/client.js | 2 +- lib/http/server.js | 9 +- lib/http/wallet.js | 6 +- lib/wallet/browser.js | 1 + lib/wallet/txdb.js | 455 ++++++++++++++++++++++++++++++++-------- lib/wallet/wallet.js | 49 ++--- lib/wallet/walletdb.js | 2 +- migrate/walletdb2to3.js | 4 +- 8 files changed, 396 insertions(+), 132 deletions(-) diff --git a/lib/http/client.js b/lib/http/client.js index 756c2feb..af8b435f 100644 --- a/lib/http/client.js +++ b/lib/http/client.js @@ -481,7 +481,7 @@ HTTPClient.prototype.getCoins = function getCoins(id, account) { * @returns {Promise} - Returns {@link TX}[]. */ -HTTPClient.prototype.getUnconfirmed = function getUnconfirmed(id, account) { +HTTPClient.prototype.getPending = function getPending(id, account) { var options = { account: account }; return this._get('/wallet/' + id + '/tx/unconfirmed', options); }; diff --git a/lib/http/server.js b/lib/http/server.js index c82427e0..f1fdb23c 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -1013,7 +1013,7 @@ HTTPServer.prototype._init = function _init() { this.get('/wallet/:id/tx/unconfirmed', con(function* (req, res, send, next) { var options = req.options; var acct = options.name || options.account; - var txs = yield req.wallet.getUnconfirmed(acct); + var txs = yield req.wallet.getPending(acct); var details; sortTX(txs); @@ -1146,6 +1146,11 @@ HTTPServer.prototype._initIO = function _initIO() { if (!utils.isHex256(token)) return callback({ error: 'Invalid parameter.' }); + if (socket.api && id === '!all') { + socket.join(id); + return callback(); + } + self.walletdb.auth(id, token).then(function(wallet) { if (!wallet) return callback({ error: 'Wallet does not exist.' }); @@ -1175,6 +1180,7 @@ HTTPServer.prototype._initIO = function _initIO() { socket.on('watch chain', function(args, callback) { if (!socket.api) return callback({ error: 'Not authorized.' }); + socket.watchChain(); callback(); }); @@ -1182,6 +1188,7 @@ HTTPServer.prototype._initIO = function _initIO() { socket.on('unwatch chain', function(args, callback) { if (!socket.api) return callback({ error: 'Not authorized.' }); + socket.unwatchChain(); callback(); }); diff --git a/lib/http/wallet.js b/lib/http/wallet.js index 6355f5e1..57e70e9b 100644 --- a/lib/http/wallet.js +++ b/lib/http/wallet.js @@ -153,11 +153,11 @@ HTTPWallet.prototype.getCoins = function getCoins(account) { }; /** - * @see Wallet#getUnconfirmed + * @see Wallet#getPending */ -HTTPWallet.prototype.getUnconfirmed = function getUnconfirmed(account) { - return this.client.getUnconfirmed(this.id, account); +HTTPWallet.prototype.getPending = function getPending(account) { + return this.client.getPending(this.id, account); }; /** diff --git a/lib/wallet/browser.js b/lib/wallet/browser.js index b2d7dcd7..fc679130 100644 --- a/lib/wallet/browser.js +++ b/lib/wallet/browser.js @@ -60,6 +60,7 @@ layout.txdb = { pre: function prefix(key) { return +key.slice(1, 11); }, + R: 'R', hi: function hi(ch, hash, index) { return ch + hash + pad32(index); }, diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 6d24e5e7..370aabb7 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -47,6 +47,7 @@ var layout = { pre: function prefix(key) { return key.readUInt32BE(1, true); }, + R: new Buffer([0x52]), hi: function hi(ch, hash, index) { var key = new Buffer(37); key[0] = ch; @@ -211,10 +212,13 @@ function TXDB(wallet) { this.logger = wallet.db.logger; this.network = wallet.db.network; this.options = wallet.db.options; - this.locked = {}; - this.coinCache = new LRU(10000); + + this.locked = {}; + this.state = null; this.balance = null; + this.pending = null; + this.events = []; } /** @@ -230,8 +234,21 @@ TXDB.layout = layout; */ TXDB.prototype.open = co(function* open() { - this.balance = yield this.getBalance(); - this.logger.info('TXDB loaded for %s.', this.wallet.id); + var state = yield this.getState(); + + if (state) { + this.state = state; + this.logger.info('TXDB loaded for %s.', this.wallet.id); + } else { + this.state = new TXDBState(this.wallet.wid, this.wallet.id); + this.logger.info('TXDB created for %s.', this.wallet.id); + } + + this.balance = this.state.balance; + + this.logger.info('TXDB State: tx=%d coin=%s.', + this.state.tx, this.state.coin); + this.logger.info( 'Balance: unconfirmed=%s confirmed=%s total=%s.', utils.btc(this.balance.unconfirmed), @@ -239,17 +256,83 @@ TXDB.prototype.open = co(function* open() { utils.btc(this.balance.total)); }); +/** + * Start batch. + * @private + */ + +TXDB.prototype.start = function start() { + this.pending = this.state.clone(); + return this.wallet.start(); +}; + +/** + * Drop batch. + * @private + */ + +TXDB.prototype.drop = function drop() { + this.pending = null; + this.events.length = 0; + return this.wallet.drop(); +}; + +/** + * Clear batch. + * @private + */ + +TXDB.prototype.clear = function clear() { + this.pending = this.state.clone(); + this.events.length = 0; + return this.wallet.clear(); +}; + +/** + * Save batch. + * @returns {Promise} + */ + +TXDB.prototype.commit = co(function* commit() { + var i, item; + + try { + yield this.wallet.commit(); + } catch (e) { + this.pending = null; + this.events.length = 0; + throw e; + } + + // Overwrite the entire state + // with our new committed state. + if (this.pending.committed) { + this.state = this.pending; + this.balance = this.state.balance; + + // Emit buffered events now that + // we know everything is written. + for (i = 0; i < this.events.length; i++) { + item = this.events[i]; + this.walletdb.emit(item[0], this.wallet.id, item[1], item[2]); + this.wallet.emit(item[0], item[1], item[2]); + } + } + + this.pending = null; + this.events.length = 0; +}); + /** * Emit transaction event. * @private * @param {String} event - * @param {TX} tx + * @param {Object} data * @param {PathInfo} info */ -TXDB.prototype.emit = function emit(event, tx, info) { - this.walletdb.emit(event, info.id, tx, info); - this.wallet.emit(event, tx, info); +TXDB.prototype.emit = function emit(event, data, info) { + this.events.push([event, data, info]); }; /** @@ -502,7 +585,7 @@ TXDB.prototype.verify = co(function* verify(tx, info) { // remove other transactions on behalf of // a non-eligible tx. if (!conflict) { - this.wallet.clear(); + this.clear(); return; } @@ -569,7 +652,7 @@ TXDB.prototype.resolveOrphans = co(function* resolveOrphans(tx, index) { // We had orphans, but they were invalid. The // balance will be (incorrectly) added outside. // Subtract to compensate. - this.balance.sub(coin); + this.pending.sub(coin); return false; }); @@ -585,16 +668,16 @@ TXDB.prototype.add = co(function* add(tx) { var info = yield this.getPathInfo(tx); var result; - this.wallet.start(); + this.start(); try { result = yield this._add(tx, info); } catch (e) { - this.wallet.drop(); + this.drop(); throw e; } - yield this.wallet.commit(); + yield this.commit(); return result; }); @@ -681,7 +764,7 @@ TXDB.prototype._add = co(function* add(tx, info) { this.del(layout.c(prevout.hash, prevout.index)); this.del(layout.C(path.account, prevout.hash, prevout.index)); this.put(layout.d(hash, i), input.coin.toRaw()); - this.balance.sub(input.coin); + this.pending.sub(input.coin); this.coinCache.remove(key); } @@ -709,7 +792,7 @@ TXDB.prototype._add = co(function* add(tx, info) { coin = Coin.fromTX(tx, i); - this.balance.add(coin); + this.pending.add(coin); coin = coin.toRaw(); @@ -727,6 +810,11 @@ TXDB.prototype._add = co(function* add(tx, info) { if (tx.ts !== 0) this.emit('confirmed', tx, info); + this.emit('balance', this.pending.balance, info); + + this.pending.tx++; + this.put(layout.R, this.pending.commit()); + return true; }); @@ -926,7 +1014,7 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { continue; } - this.balance.confirm(coin.value); + this.pending.confirm(coin.value); coin.height = tx.height; coin = coin.toRaw(); @@ -938,6 +1026,9 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { this.emit('tx', tx, info); this.emit('confirmed', tx, info); + this.emit('balance', this.pending.balance, info); + + this.put(layout.R, this.pending.commit()); return true; }); @@ -951,16 +1042,16 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { TXDB.prototype.remove = co(function* remove(hash) { var result; - this.wallet.start(); + this.start(); try { result = yield this._remove(hash); } catch (e) { - this.wallet.drop(); + this.drop(); throw e; } - yield this.wallet.commit(); + yield this.commit(); return result; }); @@ -1055,7 +1146,7 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { if (!path) continue; - this.balance.add(input.coin); + this.pending.add(input.coin); coin = input.coin.toRaw(); @@ -1081,7 +1172,7 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { coin = Coin.fromTX(tx, i); - this.balance.sub(coin); + this.pending.sub(coin); this.del(layout.c(hash, i)); this.del(layout.C(path.account, hash, i)); @@ -1090,6 +1181,10 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { } this.emit('remove tx', tx, info); + this.emit('balance', this.pending.balance, info); + + this.pending.tx--; + this.put(layout.R, this.pending.commit()); return info; }); @@ -1103,16 +1198,16 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { TXDB.prototype.unconfirm = co(function* unconfirm(hash) { var result; - this.wallet.start(); + this.start(); try { result = yield this._unconfirm(hash); } catch (e) { - this.wallet.drop(); + this.drop(); throw e; } - yield this.wallet.commit(); + yield this.commit(); return result; }); @@ -1183,7 +1278,7 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { continue; } - this.balance.unconfirm(coin.value); + this.pending.unconfirm(coin.value); coin.height = tx.height; coin = coin.toRaw(); @@ -1194,6 +1289,9 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { } this.emit('unconfirmed', tx, info); + this.emit('balance', this.pending.balance, info); + + this.put(layout.R, this.pending.commit()); return info; }); @@ -1303,6 +1401,23 @@ TXDB.prototype.getLocked = function getLocked() { return outpoints; }; +/** + * Get hashes of all transactions in the database. + * @param {Number?} account + * @returns {Promise} - Returns {@link Hash}[]. + */ + +TXDB.prototype.getAccountHistoryHashes = function getHistoryHashes(account) { + return this.keys({ + gte: layout.T(account, constants.NULL_HASH), + lte: layout.T(account, constants.HIGH_HASH), + parse: function(key) { + key = layout.Tt(key); + return key[1]; + } + }); +}; + /** * Get hashes of all transactions in the database. * @param {Number?} account @@ -1310,16 +1425,8 @@ TXDB.prototype.getLocked = function getLocked() { */ TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) { - if (account != null) { - return this.keys({ - gte: layout.T(account, constants.NULL_HASH), - lte: layout.T(account, constants.HIGH_HASH), - parse: function(key) { - key = layout.Tt(key); - return key[1]; - } - }); - } + if (account != null) + return this.getAccountHistoryHashes(account); return this.keys({ gte: layout.t(constants.NULL_HASH), @@ -1334,17 +1441,26 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) { * @returns {Promise} - Returns {@link Hash}[]. */ -TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(account) { - if (account != null) { - return this.keys({ - gte: layout.P(account, constants.NULL_HASH), - lte: layout.P(account, constants.HIGH_HASH), - parse: function(key) { - key = layout.Pp(key); - return key[1]; - } - }); - } +TXDB.prototype.getAccountPendingHashes = function getAccountPendingHashes(account) { + return this.keys({ + gte: layout.P(account, constants.NULL_HASH), + lte: layout.P(account, constants.HIGH_HASH), + parse: function(key) { + key = layout.Pp(key); + return key[1]; + } + }); +}; + +/** + * Get hashes of all unconfirmed transactions in the database. + * @param {Number?} account + * @returns {Promise} - Returns {@link Hash}[]. + */ + +TXDB.prototype.getPendingHashes = function getPendingHashes(account) { + if (account != null) + return this.getAccountPendingHashes(account); return this.keys({ gte: layout.p(constants.NULL_HASH), @@ -1353,6 +1469,23 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(account) { }); }; +/** + * Get all coin hashes in the database. + * @param {Number?} account + * @returns {Promise} - Returns {@link Hash}[]. + */ + +TXDB.prototype.getAccountOutpoints = function getAccountOutpoints(account) { + return this.keys({ + gte: layout.C(account, constants.NULL_HASH, 0), + lte: layout.C(account, constants.HIGH_HASH, 0xffffffff), + parse: function(key) { + key = layout.Cc(key); + return new Outpoint(key[1], key[2]); + } + }); +}; + /** * Get all coin hashes in the database. * @param {Number?} account @@ -1360,16 +1493,8 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(account) { */ TXDB.prototype.getOutpoints = function getOutpoints(account) { - if (account != null) { - return this.keys({ - gte: layout.C(account, constants.NULL_HASH, 0), - lte: layout.C(account, constants.HIGH_HASH, 0xffffffff), - parse: function(key) { - key = layout.Cc(key); - return new Outpoint(key[1], key[2]); - } - }); - } + if (account != null) + return this.getAccountOutpoints(account); return this.keys({ gte: layout.c(constants.NULL_HASH, 0), @@ -1438,6 +1563,33 @@ TXDB.prototype.getHeightHashes = function getHeightHashes(height) { return this.getHeightRangeHashes({ start: height, end: height }); }; +/** + * Get TX hashes by timestamp range. + * @param {Number?} account + * @param {Object} options + * @param {Number} options.start - Start height. + * @param {Number} options.end - End height. + * @param {Number?} options.limit - Max number of records. + * @param {Boolean?} options.reverse - Reverse order. + * @returns {Promise} - Returns {@link Hash}[]. + */ + +TXDB.prototype.getAccountRangeHashes = function getAccountRangeHashes(account, options) { + var start = options.start || 0; + var end = options.end || 0xffffffff; + + return this.keys({ + gte: layout.M(account, start, constants.NULL_HASH), + lte: layout.M(account, end, constants.HIGH_HASH), + limit: options.limit, + reverse: options.reverse, + parse: function(key) { + key = layout.Mm(key); + return key[2]; + } + }); +}; + /** * Get TX hashes by timestamp range. * @param {Number?} account @@ -1457,22 +1609,12 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(account, options) { account = null; } + if (account != null) + return this.getAccountRangeHashes(account, options); + start = options.start || 0; end = options.end || 0xffffffff; - if (account != null) { - return this.keys({ - gte: layout.M(account, start, constants.NULL_HASH), - lte: layout.M(account, end, constants.HIGH_HASH), - limit: options.limit, - reverse: options.reverse, - parse: function(key) { - key = layout.Mm(key); - return key[2]; - } - }); - } - return this.keys({ gte: layout.m(start, constants.NULL_HASH), lte: layout.m(end, constants.HIGH_HASH), @@ -1586,11 +1728,11 @@ TXDB.prototype.getAccountHistory = co(function* getAccountHistory(account) { * @returns {Promise} - Returns {@link TX}[]. */ -TXDB.prototype.getUnconfirmed = co(function* getUnconfirmed(account) { +TXDB.prototype.getPending = co(function* getPending(account) { var txs = []; var i, hashes, hash, tx; - hashes = yield this.getUnconfirmedHashes(account); + hashes = yield this.getPendingHashes(account); for (i = 0; i < hashes.length; i++) { hash = hashes[i]; @@ -1717,6 +1859,20 @@ TXDB.prototype.fillCoins = co(function* fillCoins(tx) { return tx; }); +/** + * Get TXDB state. + * @returns {Promise} + */ + +TXDB.prototype.getState = co(function* getState() { + var data = yield this.get(layout.R); + + if (!data) + return; + + return TXDBState.fromRaw(this.wallet.wid, this.wallet.id, data); +}); + /** * Get transaction. * @param {Hash} hash @@ -1894,19 +2050,23 @@ TXDB.prototype.hasCoin = function hasCoin(hash, index) { */ TXDB.prototype.getBalance = co(function* getBalance(account) { - var self = this; - var balance; - // Slow case if (account != null) return yield this.getAccountBalance(account); - // Really fast case - if (this.balance) - return this.balance; - // Fast case - balance = new Balance(this.wallet); + return this.balance; +}); + +/** + * Calculate balance. + * @param {Number?} account + * @returns {Promise} - Returns {@link Balance}. + */ + +TXDB.prototype.getWalletBalance = co(function* getWalletBalance(account) { + var self = this; + var balance = new Balance(this.wallet.wid, this.wallet.id, -1); yield this.range({ gte: layout.c(constants.NULL_HASH, 0x00000000), @@ -1932,7 +2092,7 @@ TXDB.prototype.getBalance = co(function* getBalance(account) { TXDB.prototype.getAccountBalance = co(function* getBalance(account) { var prevout = yield this.getOutpoints(account); - var balance = new Balance(this.wallet); + var balance = new Balance(this.wallet.wid, this.wallet.id, account); var i, ckey, key, coin, op, data; for (i = 0; i < prevout.length; i++) { @@ -1988,7 +2148,7 @@ TXDB.prototype.zap = co(function* zap(account, age) { assert(now - tx.ps >= age); - this.wallet.start(); + this.start(); this.logger.debug('Zapping TX: %s (%s)', hash, this.wallet.id); @@ -1996,13 +2156,13 @@ TXDB.prototype.zap = co(function* zap(account, age) { try { yield this._remove(hash); } catch (e) { - this.wallet.drop(); + this.drop(); throw e; } hashes.push(hash); - yield this.wallet.commit(); + yield this.commit(); } return hashes; @@ -2025,17 +2185,33 @@ TXDB.prototype.abandon = co(function* abandon(hash) { * Balance */ -function Balance(wallet) { +function Balance(wid, id, account) { if (!(this instanceof Balance)) - return new Balance(wallet); + return new Balance(wid, id, account); - this.wid = wallet.wid; - this.id = wallet.id; + this.wid = wid; + this.id = id; + this.account = account; this.unconfirmed = 0; this.confirmed = 0; this.total = 0; } +Balance.prototype.clone = function clone() { + var balance = new Balance(this.wid, this.id, this.account); + balance.unconfirmed = this.unconfirmed; + balance.confirmed = this.confirmed; + balance.total = this.total; + return balance; +}; + +Balance.prototype.equal = function equal(balance) { + return this.wid === balance.wid + && this.total === balance.total + && this.confirmed === balance.confirmed + && this.unconfirmed === balance.unconfirmed; +}; + Balance.prototype.add = function add(coin) { this.total += coin.value; if (coin.height === -1) @@ -2076,10 +2252,36 @@ Balance.prototype.addRaw = function addRaw(data) { this.confirmed += value; }; +Balance.prototype.toRaw = function toRaw(writer) { + var p = new BufferWriter(writer); + + p.writeU64(this.unconfirmed); + p.writeU64(this.confirmed); + + if (!writer) + p = p.render(); + + return p; +}; + +Balance.prototype.fromRaw = function fromRaw(data) { + var p = new BufferReader(data); + this.unconfirmed = p.readU53(); + this.confirmed = p.readU53(); + this.total += this.unconfirmed + this.total += this.confirmed; + return this; +}; + +Balance.fromRaw = function fromRaw(wid, id, data) { + return new Balance(wid, id, -1).fromRaw(data); +}; + Balance.prototype.toJSON = function toJSON() { return { wid: this.wid, id: this.id, + account: this.account, unconfirmed: utils.btc(this.unconfirmed), confirmed: utils.btc(this.confirmed), total: utils.btc(this.total) @@ -2094,6 +2296,85 @@ Balance.prototype.toString = function toString() { + '>'; }; +/** + * Chain State + * @constructor + */ + +function TXDBState(wid, id) { + this.wid = wid; + this.id = id; + this.tx = 0; + this.coin = 0; + this.balance = new Balance(wid, id, -1); + this.committed = false; +} + +TXDBState.prototype.clone = function clone() { + var state = new TXDBState(); + state.wid = this.wid; + state.id = this.id; + state.tx = this.tx; + state.coin = this.coin; + state.balance = this.balance.clone(); + return state; +}; + +TXDBState.prototype.commit = function commit() { + this.committed = true; + return this.toRaw(); +}; + +TXDBState.prototype.toRaw = function toRaw() { + var p = new BufferWriter(); + p.writeU64(this.tx); + p.writeU64(this.coin); + this.balance.toRaw(p); + return p.render(); +}; + +TXDBState.prototype.fromRaw = function fromRaw(data) { + var p = new BufferReader(data); + this.tx = p.readU53(); + this.coin = p.readU53(); + this.balance.fromRaw(p); + return this; +}; + +TXDBState.fromRaw = function fromRaw(wid, id, data) { + return new TXDBState(wid, id).fromRaw(data); +}; + +TXDBState.prototype.add = function add(coin) { + this.coin++; + return this.balance.add(coin); +}; + +TXDBState.prototype.sub = function sub(coin) { + this.coin--; + return this.balance.sub(coin); +}; + +TXDBState.prototype.confirm = function confirm(value) { + return this.balance.confirm(value); +}; + +TXDBState.prototype.unconfirm = function unconfirm(value) { + return this.balance.unconfirm(value); +}; + +TXDBState.prototype.toJSON = function toJSON() { + return { + wid: this.wid, + id: this.id, + tx: this.tx, + coin: this.coin, + unconfirmed: utils.btc(this.balance.unconfirmed), + confirmed: utils.btc(this.balance.confirmed), + total: utils.btc(this.balance.total) + }; +}; + /* * Helpers */ diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 59c0bd3f..6fca8bdf 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -1482,7 +1482,7 @@ Wallet.prototype._send = co(function* send(options, passphrase) { */ Wallet.prototype.resend = co(function* resend() { - var txs = yield this.getUnconfirmed(); + var txs = yield this.getPending(); var i; if (txs.length > 0) @@ -1686,36 +1686,9 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) { derived.push(ring); } - if (derived.length > 0) { - this.db.emit('address', this.id, derived); - this.emit('address', derived); - } - return derived; }); -/** - * Emit balance events after a tx is saved. - * @private - * @param {TX} tx - * @param {PathInfo} info - * @returns {Promise} - */ - -Wallet.prototype.updateBalances = co(function* updateBalances() { - var balance; - - if (this.db.listeners('balance').length === 0 - && this.listeners('balance').length === 0) { - return; - } - - balance = yield this.getBalance(); - - this.db.emit('balance', this.id, balance); - this.emit('balance', balance); -}); - /** * Get a redeem script or witness script by hash. * @param {Hash} hash - Can be a ripemd160 or a sha256. @@ -1856,20 +1829,24 @@ Wallet.prototype.add = co(function* add(tx) { Wallet.prototype._add = co(function* add(tx) { var info = yield this.getPathInfo(tx); - var result; + var result, derived; - this.start(); + this.txdb.start(); try { result = yield this.txdb._add(tx, info); - yield this.syncOutputDepth(info); - yield this.updateBalances(); + derived = yield this.syncOutputDepth(info); } catch (e) { - this.drop(); + this.txdb.drop(); throw e; } - yield this.commit(); + yield this.txdb.commit(); + + if (derived.length > 0) { + this.db.emit('address', this.id, derived); + this.emit('address', derived); + } return result; }); @@ -2044,9 +2021,9 @@ Wallet.prototype.getCoins = co(function* getCoins(acct) { * @returns {Promise} - Returns {@link TX}[]. */ -Wallet.prototype.getUnconfirmed = co(function* getUnconfirmed(acct) { +Wallet.prototype.getPending = co(function* getPending(acct) { var account = yield this.ensureIndex(acct); - return yield this.txdb.getUnconfirmed(account); + return yield this.txdb.getPending(account); }); /** diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index a9054ab7..c39737b8 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -207,7 +207,7 @@ WalletDB.prototype._init = function _init() { WalletDB.prototype._open = co(function* open() { yield this.db.open(); - yield this.db.checkVersion('V', 3); + yield this.db.checkVersion('V', 4); yield this.writeGenesis(); this.depth = yield this.getDepth(); diff --git a/migrate/walletdb2to3.js b/migrate/walletdb2to3.js index d4c6d8bd..0c890523 100644 --- a/migrate/walletdb2to3.js +++ b/migrate/walletdb2to3.js @@ -34,9 +34,7 @@ var updateVersion = co(function* updateVersion() { console.log('Checking version.'); data = yield db.get('V'); - - if (!data) - return; + assert(data, 'No version.'); ver = data.readUInt32LE(0, true);