diff --git a/lib/http/server.js b/lib/http/server.js index d295eeae..7d483a52 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -1259,32 +1259,27 @@ HTTPServer.prototype._initIO = function _initIO() { }); }); - this.walletdb.on('tx', function(id, tx, info) { - var details = info.toJSON(); + this.walletdb.on('tx', function(id, tx, details) { self.server.io.to(id).emit('wallet tx', details); self.server.io.to('!all').emit('wallet tx', id, details); }); - this.walletdb.on('confirmed', function(id, tx, info) { - var details = info.toJSON(); + this.walletdb.on('confirmed', function(id, tx, details) { self.server.io.to(id).emit('wallet confirmed', details); self.server.io.to('!all').emit('wallet confirmed', id, details); }); - this.walletdb.on('unconfirmed', function(id, tx, info) { - var details = info.toJSON(); + this.walletdb.on('unconfirmed', function(id, tx, details) { self.server.io.to(id).emit('wallet unconfirmed', details); self.server.io.to('!all').emit('wallet unconfirmed', id, details); }); - this.walletdb.on('conflict', function(id, tx, info) { - var details = info.toJSON(); + this.walletdb.on('conflict', function(id, tx, details) { self.server.io.to(id).emit('wallet conflict', details); self.server.io.to('!all').emit('wallet conflict', id, details); }); this.walletdb.on('balance', function(id, balance) { - var json = balance.toJSON(); self.server.io.to(id).emit('wallet balance', json); self.server.io.to('!all').emit('wallet balance', id, json); }); diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 380fee7d..38c3ae98 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -331,11 +331,11 @@ TXDB.prototype.commit = co(function* commit() { * @private * @param {String} event * @param {Object} data - * @param {PathInfo} info + * @param {PathInfo} details */ -TXDB.prototype.emit = function emit(event, data, info) { - this.events.push([event, data, info]); +TXDB.prototype.emit = function emit(event, data, details) { + this.events.push([event, data, details]); }; /** @@ -436,8 +436,22 @@ TXDB.prototype.values = function values(options) { * @returns {Promise} - Returns {@link PathInfo}. */ -TXDB.prototype.getPathInfo = function getPathInfo(tx) { - return this.wallet.getPathInfo(tx); +TXDB.prototype.getPath = function getPath(output) { + if (!output) + return Promise.resolve(); + return this.wallet.getPath(output.getAddress()); +}; + +/** + * Map a transactions' addresses to wallet IDs. + * @param {TX} tx + * @returns {Promise} - Returns {@link PathInfo}. + */ + +TXDB.prototype.hasPath = function hasPath(output) { + if (!output) + return Promise.resolve(); + return this.wallet.hasPath(output.getAddress()); }; /** @@ -478,7 +492,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) { var hasOrphans = false; var orphans = []; var i, input, prevout; - var path, key, coin, spent; + var key, coin, spent; if (tx.isCoinbase()) return true; @@ -519,12 +533,8 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) { continue; } - path = yield this.wallet.hasPath(input.getAddress()); - - if (!path) - continue; - - orphans[i] = true; + if (yield this.hasPath(input)) + orphans[i] = true; } for (i = 0; i < tx.inputs.length; i++) { @@ -627,11 +637,10 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, resolved) { * double spenders, and verify inputs. * @private * @param {TX} tx - * @param {PathInfo} info * @returns {Promise} */ -TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, info) { +TXDB.prototype.removeConflicts = co(function* removeConflicts(tx) { var hash = tx.hash('hex'); var i, input, prevout, spent; @@ -660,18 +669,16 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, info) { /** * Add transaction, runs `confirm()` and `verify()`. * @param {TX} tx - * @param {PathInfo} info * @returns {Promise} */ TXDB.prototype.add = co(function* add(tx) { - var info = yield this.getPathInfo(tx); var result; this.start(); try { - result = yield this._add(tx, info); + result = yield this._add(tx); } catch (e) { this.drop(); throw e; @@ -686,43 +693,53 @@ TXDB.prototype.add = co(function* add(tx) { * Add transaction without a lock. * @private * @param {TX} tx - * @param {PathInfo} info * @returns {Promise} */ -TXDB.prototype._add = co(function* add(tx, info) { +TXDB.prototype._add = co(function* add(tx) { var hash = tx.hash('hex'); - var path, account; - var i, result, input, output, coin; - var prevout, key, spender, raw; + var path, account, existing; + var i, input, output, coin; + var prevout, key, spender, raw, details; assert(!tx.mutable, 'Cannot add mutable TX to wallet.'); + existing = yield this.getTX(hash); + + if (existing) { + // Existing tx is already confirmed. Ignore. + if (existing.height !== -1) + return; + + // The incoming tx won't confirm the + // existing one anyway. Ignore. + if (tx.height === -1) + return; + + // Attempt to confirm tx before adding it. + return yield this.confirm(tx, existing); + } + if (tx.height === -1) { // We ignore double spends from the mempool. if (yield this.isDoubleSpend(tx)) - return false; + return; // We ignore any unconfirmed txs // that are replace-by-fee. if (yield this.isRBF(tx)) { this.put(layout.r(hash), DUMMY); - return false; + return; } } else { // This potentially removes double-spenders. - yield this.removeConflicts(tx, info); + yield this.removeConflicts(tx); // Delete the replace-by-fee record. this.del(layout.r(hash)); } - // Attempt to confirm tx before adding it. - result = yield this.confirm(tx, info); - - // Ignore if we already have this tx. - if (result) - return true; + details = new Details(this, tx); this.put(layout.t(hash), tx.toExtended()); @@ -733,19 +750,6 @@ TXDB.prototype._add = co(function* add(tx, info) { this.put(layout.m(tx.ps, hash), DUMMY); - for (i = 0; i < info.accounts.length; i++) { - account = info.accounts[i]; - - this.put(layout.T(account, hash), DUMMY); - - if (tx.height === -1) - this.put(layout.P(account, hash), DUMMY); - else - this.put(layout.H(account, tx.height, hash), DUMMY); - - this.put(layout.M(account, tx.ps, hash), DUMMY); - } - // Consume unspent money or add orphans if (!tx.isCoinbase()) { for (i = 0; i < tx.inputs.length; i++) { @@ -757,9 +761,11 @@ TXDB.prototype._add = co(function* add(tx, info) { if (!coin) continue; - path = info.getPath(coin); + path = yield this.getPath(coin); assert(path); + details.addInput(i, path, coin); + key = prevout.hash + prevout.index; spender = Outpoint.fromTX(tx, i).toRaw(); @@ -786,7 +792,7 @@ TXDB.prototype._add = co(function* add(tx, info) { // Add unspent outputs or resolve orphans. for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; - path = info.getPath(output); + path = yield this.getPath(output); key = hash + i; // Do not add unspents for @@ -794,6 +800,8 @@ TXDB.prototype._add = co(function* add(tx, info) { if (!path) continue; + details.addOutput(i, path); + coin = Coin.fromTX(tx, i); raw = coin.toRaw(); @@ -809,20 +817,33 @@ TXDB.prototype._add = co(function* add(tx, info) { this.coinCache.set(key, raw); } + for (i = 0; i < details.accounts.length; i++) { + account = details.accounts[i]; + + this.put(layout.T(account, hash), DUMMY); + + if (tx.height === -1) + this.put(layout.P(account, hash), DUMMY); + else + this.put(layout.H(account, tx.height, hash), DUMMY); + + this.put(layout.M(account, tx.ps, hash), DUMMY); + } + this.pending.tx++; this.put(layout.R, this.pending.commit()); // Clear any locked coins to free up memory. this.unlockTX(tx); - this.emit('tx', tx, info); + this.emit('tx', tx, details); if (tx.height !== -1) - this.emit('confirmed', tx, info); + this.emit('confirmed', tx, details); - this.emit('balance', this.pending.toBalance(), info); + this.emit('balance', this.pending.toBalance(), details); - return true; + return details; }); /** @@ -839,7 +860,7 @@ TXDB.prototype._add = co(function* add(tx, info) { TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) { var tx = yield this.getTX(hash); - var info; + var details; assert(tx); @@ -847,14 +868,16 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) { this.drop(); - info = yield this.removeRecursive(tx); + details = yield this.removeRecursive(tx); this.start(); this.logger.warning('Removed conflict: %s.', tx.rhash); // Emit the _removed_ transaction. - this.emit('conflict', tx, info); + this.emit('conflict', tx, details); + + return details; }); /** @@ -867,7 +890,7 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) { TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) { var hash = tx.hash('hex'); - var i, spent, stx, info; + var i, spent, stx, details; for (i = 0; i < tx.outputs.length; i++) { spent = yield this.getSpent(hash, i); @@ -886,13 +909,13 @@ TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) { this.start(); // Remove the spender. - info = yield this.lazyRemove(tx); + details = yield this.lazyRemove(tx); - assert(info); + assert(details); yield this.commit(); - return info; + return details; }); /** @@ -1012,32 +1035,16 @@ TXDB.prototype.isSpending = co(function* isSpending(hash, index) { * Attempt to confirm a transaction. * @private * @param {TX} tx - * @param {AddressMap} info * @returns {Promise} - Returns Boolean. `false` if * the transaction should be added to the database, `true` if the * transaction was confirmed, or should be ignored. */ -TXDB.prototype.confirm = co(function* confirm(tx, info) { +TXDB.prototype.confirm = co(function* confirm(tx, existing) { var hash = tx.hash('hex'); - var i, account, existing, output, coin; + var i, account, output, coin; var input, prevout, path, spender, coins; - var key, raw; - - existing = yield this.getTX(hash); - - // Haven't seen this tx before, add it. - if (!existing) - return false; - - // Existing tx is already confirmed. Ignore. - if (existing.height !== -1) - return true; - - // The incoming tx won't confirm the - // existing one anyway. Ignore. - if (tx.height === -1) - return true; + var key, raw, details; // Inject block properties. existing.ts = tx.ts; @@ -1046,17 +1053,13 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { existing.block = tx.block; tx = existing; + details = new Details(this, tx); + this.put(layout.t(hash), tx.toExtended()); this.del(layout.p(hash)); this.put(layout.h(tx.height, hash), DUMMY); - for (i = 0; i < info.accounts.length; i++) { - account = info.accounts[i]; - this.del(layout.P(account, hash)); - this.put(layout.H(account, tx.height, hash), DUMMY); - } - // Consume unspent money or add orphans if (!tx.isCoinbase()) { coins = yield this.fillHistory(tx); @@ -1082,10 +1085,14 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { assert(coin.height !== -1); + input.coin = coin; + // Only bother if this input is ours. - path = info.getPath(coin); + path = yield this.getPath(coin); assert(path); + details.addInput(i, path, coin); + key = prevout.hash + prevout.index; this.del(layout.S(prevout.hash, prevout.index)); @@ -1102,12 +1109,15 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; + path = yield this.getPath(output); key = hash + i; // Only update coins if this output is ours. - if (!info.hasPath(output)) + if (!path) continue; + details.addOutput(i, path); + // Update spent coin. yield this.updateSpentCoin(tx, i); @@ -1127,16 +1137,22 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { this.coinCache.set(key, raw); } + for (i = 0; i < details.accounts.length; i++) { + account = details.accounts[i]; + this.del(layout.P(account, hash)); + this.put(layout.H(account, tx.height, hash), DUMMY); + } + this.put(layout.R, this.pending.commit()); // Clear any locked coins to free up memory. this.unlockTX(tx); - this.emit('tx', tx, info); - this.emit('confirmed', tx, info); - this.emit('balance', this.pending.toBalance(), info); + this.emit('tx', tx, details); + this.emit('confirmed', tx, details); + this.emit('balance', this.pending.toBalance(), details); - return true; + return details; }); /** @@ -1146,12 +1162,12 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) { */ TXDB.prototype.remove = co(function* remove(hash) { - var result; + var details; this.start(); try { - result = yield this._remove(hash); + details = yield this._remove(hash); } catch (e) { this.drop(); throw e; @@ -1159,7 +1175,7 @@ TXDB.prototype.remove = co(function* remove(hash) { yield this.commit(); - return result; + return details; }); /** @@ -1171,21 +1187,21 @@ TXDB.prototype.remove = co(function* remove(hash) { TXDB.prototype._remove = co(function* remove(hash) { var tx = yield this.getTX(hash); - var info; + var details; if (!tx) return; this.drop(); - info = yield this.removeRecursive(tx); + details = yield this.removeRecursive(tx); this.start(); - if (!info) + if (!details) return; - return info; + return details; }); /** @@ -1197,25 +1213,22 @@ TXDB.prototype._remove = co(function* remove(hash) { */ TXDB.prototype.lazyRemove = co(function* lazyRemove(tx) { - var info = yield this.getPathInfo(tx); - if (!info) - return; - - return yield this.__remove(tx, info); + return yield this.__remove(tx); }); /** * Remove a transaction from the database. Disconnect inputs. * @private * @param {TX} tx - * @param {AddressMap} info * @returns {Promise} */ -TXDB.prototype.__remove = co(function* remove(tx, info) { +TXDB.prototype.__remove = co(function* remove(tx) { var hash = tx.hash('hex'); var i, path, account, key, prevout; - var input, output, coin, coins, raw; + var input, output, coin, coins, raw, details; + + details = new Details(this, tx); this.del(layout.t(hash)); @@ -1226,19 +1239,6 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { this.del(layout.m(tx.ps, hash)); - for (i = 0; i < info.accounts.length; i++) { - account = info.accounts[i]; - - this.del(layout.T(account, hash)); - - if (tx.height === -1) - this.del(layout.P(account, hash)); - else - this.del(layout.H(account, tx.height, hash)); - - this.del(layout.M(account, tx.ps, hash)); - } - if (!tx.isCoinbase()) { coins = yield this.fillHistory(tx); @@ -1251,9 +1251,11 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { if (!coin) continue; - path = info.getPath(coin); + path = yield this.getPath(coin); assert(path); + details.addInput(i, path, coin); + this.pending.unconfirmed += coin.value; this.del(layout.s(prevout.hash, prevout.index)); @@ -1277,11 +1279,13 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; key = hash + i; - path = info.getPath(output); + path = yield this.getPath(output); if (!path) continue; + details.addOutput(i, path); + this.pending.coin--; this.pending.unconfirmed -= output.value; @@ -1294,13 +1298,26 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { this.coinCache.remove(key); } + for (i = 0; i < details.accounts.length; i++) { + account = details.accounts[i]; + + this.del(layout.T(account, hash)); + + if (tx.height === -1) + this.del(layout.P(account, hash)); + else + this.del(layout.H(account, tx.height, hash)); + + this.del(layout.M(account, tx.ps, hash)); + } + this.pending.tx--; this.put(layout.R, this.pending.commit()); - this.emit('remove tx', tx, info); - this.emit('balance', this.pending.toBalance(), info); + this.emit('remove tx', tx, details); + this.emit('balance', this.pending.toBalance(), details); - return info; + return details; }); /** @@ -1310,12 +1327,12 @@ TXDB.prototype.__remove = co(function* remove(tx, info) { */ TXDB.prototype.unconfirm = co(function* unconfirm(hash) { - var result; + var details; this.start(); try { - result = yield this._unconfirm(hash); + details = yield this._unconfirm(hash); } catch (e) { this.drop(); throw e; @@ -1323,7 +1340,7 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash) { yield this.commit(); - return result; + return details; }); /** @@ -1335,29 +1352,23 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash) { TXDB.prototype._unconfirm = co(function* unconfirm(hash) { var tx = yield this.getTX(hash); - var info, result; + var details; if (!tx) return false; - info = yield this.getPathInfo(tx); + details = yield this.__unconfirm(tx); - if (!info) - return false; - - result = yield this.__unconfirm(tx, info); - - return result; + return details; }); /** * Unconfirm a transaction. This is usually necessary after a reorg. * @param {Hash} hash - * @param {AddressMap} info * @returns {Promise} */ -TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { +TXDB.prototype.__unconfirm = co(function* unconfirm(tx) { var hash = tx.hash('hex'); var height = tx.height; var i, account, output, key, coin, coins; @@ -1368,17 +1379,13 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { tx.unsetBlock(); + details = new Details(this, tx); + this.put(layout.t(hash), tx.toExtended()); this.put(layout.p(hash), DUMMY); this.del(layout.h(height, hash)); - for (i = 0; i < info.accounts.length; i++) { - account = info.accounts[i]; - this.put(layout.P(account, hash), DUMMY); - this.del(layout.H(account, height, hash)); - } - // Consume unspent money or add orphans if (!tx.isCoinbase()) { coins = yield this.fillHistory(tx); @@ -1396,9 +1403,11 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { raw = coin.toRaw(); - path = info.getPath(coin); + path = yield this.getPath(coin); assert(path); + details.addInput(i, path, coin); + key = prevout.hash + prevout.index; spender = Outpoint.fromTX(tx, i).toRaw(); @@ -1417,6 +1426,7 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; + path = yield this.getPath(output); key = hash + i; // Update spent coin. @@ -1427,6 +1437,8 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { if (!coin) continue; + details.addOutput(i, path); + coin.height = -1; raw = coin.toRaw(); @@ -1438,12 +1450,18 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) { this.coinCache.set(key, raw); } + for (i = 0; i < details.accounts.length; i++) { + account = details.accounts[i]; + this.put(layout.P(account, hash), DUMMY); + this.del(layout.H(account, height, hash)); + } + this.put(layout.R, this.pending.commit()); - this.emit('unconfirmed', tx, info); - this.emit('balance', this.pending.toBalance(), info); + this.emit('unconfirmed', tx, details); + this.emit('balance', this.pending.toBalance(), details); - return info; + return details; }); /** @@ -2105,7 +2123,8 @@ TXDB.prototype.getDetails = co(function* getDetails(hash) { */ TXDB.prototype.toDetails = co(function* toDetails(tx) { - var i, out, txs, details, info; + var i, out, txs, details; + var coins, coin, path, input, output; if (Array.isArray(tx)) { out = []; @@ -2124,14 +2143,23 @@ TXDB.prototype.toDetails = co(function* toDetails(tx) { return out; } - yield this.fillHistory(tx); + details = new Details(this, tx); - info = yield this.getPathInfo(tx); + coins = yield this.fillHistory(tx); - if (!info) - throw new Error('Info not found.'); + for (i = 0; i < tx.inputs.length; i++) { + coin = coins[i]; + path = yield this.getPath(coin); + details.addInput(i, path, coin); + } - return info.toDetails(); + for (i = 0; i < tx.outputs.length; i++) { + output = tx.outputs[i]; + path = yield this.getPath(output); + details.addOutput(i, path); + } + + return details; }); /** @@ -2483,6 +2511,147 @@ function Orphan(tx, i) { this.index = i; } +function cmp(a, b) { + return a - b; +} + +/** + * Transaction Details + * @constructor + * @param {PathInfo} info + */ + +function Details(txdb, tx) { + if (!(this instanceof Details)) + return new Details(txdb, tx); + + this.db = txdb.walletdb; + this.network = this.db.network; + this.wid = txdb.wallet.wid; + this.id = txdb.wallet.id; + this.hash = tx.hash('hex'); + this.height = tx.height; + this.block = tx.block; + this.index = tx.index; + this.confirmations = tx.getConfirmations(this.db.height); + this.fee = tx.getFee(); + this.ts = tx.ts; + this.ps = tx.ps; + this.tx = tx; + + this.inputs = []; + this.outputs = []; + this.accounts = []; + + this.init(); +} + +Details.prototype.init = function init() { + var i, input, output, member; + + for (i = 0; i < this.tx.inputs.length; i++) { + input = this.tx.inputs[i]; + member = new DetailsMember(); + member.address = input.getAddress(); + this.inputs.push(member); + } + + for (i = 0; i < this.tx.outputs.length; i++) { + output = this.tx.outputs[i]; + member = new DetailsMember(); + member.value = output.value; + member.address = output.getAddress(); + this.outputs.push(member); + } +}; + +Details.prototype.addInput = function addInput(i, path, coin) { + var member = this.inputs[i]; + + if (coin) { + member.value = coin.value; + member.address = coin.getAddress(); + } + + if (path) { + member.path = path; + utils.binaryInsert(this.accounts, path.account, cmp, true); + } +}; + +Details.prototype.addOutput = function addOutput(i, path) { + var member = this.outputs[i]; + + if (path) { + member.path = path; + utils.binaryInsert(this.accounts, path.account, cmp, true); + } +}; + +/** + * Convert details to a more json-friendly object. + * @returns {Object} + */ + +Details.prototype.toJSON = function toJSON() { + var self = this; + return { + wid: this.wid, + id: this.id, + hash: utils.revHex(this.hash), + height: this.height, + block: this.block ? utils.revHex(this.block) : null, + ts: this.ts, + ps: this.ps, + index: this.index, + fee: utils.btc(this.fee), + confirmations: this.confirmations, + inputs: this.inputs.map(function(input) { + return input.toJSON(self.network); + }), + outputs: this.outputs.map(function(output) { + return output.toJSON(self.network); + }), + tx: this.tx.toRaw().toString('hex') + }; +}; + +/** + * Transaction Details Member + * @constructor + * @property {Number} value + * @property {Address} address + * @property {Path} path + */ + +function DetailsMember() { + if (!(this instanceof DetailsMember)) + return new DetailsMember(); + + this.value = 0; + this.address = null; + this.path = null; +} + +/** + * Convert the member to a more json-friendly object. + * @param {Network} network + * @returns {Object} + */ + +DetailsMember.prototype.toJSON = function toJSON(network) { + return { + value: utils.btc(this.value), + address: this.address + ? this.address.toBase58(network) + : null, + path: this.path + ? this.path.toJSON() + : null + }; +}; + + /* * Expose */ diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 5da88dcd..8a35f26b 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -1622,14 +1622,20 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) { * (true if new addresses were allocated). */ -Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) { +Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(details) { var derived = []; var accounts = {}; var i, j, path, paths, account; var receive, change, nested, ring; - for (i = 0; i < info.paths.length; i++) { - path = info.paths[i]; + if (!details) + return derived; + + for (i = 0; i < details.outputs.length; i++) { + path = details.outputs[i].path; + + if (!path) + continue; if (path.index === -1) continue; @@ -1842,14 +1848,13 @@ Wallet.prototype._add = co(function* add(tx) { */ Wallet.prototype._insert = co(function* insert(tx) { - var info = yield this.getPathInfo(tx); - var result, derived; + var details, derived; this.txdb.start(); try { - result = yield this.txdb._add(tx, info); - derived = yield this.syncOutputDepth(info); + details = yield this.txdb._add(tx); + derived = yield this.syncOutputDepth(details); } catch (e) { this.txdb.drop(); throw e; @@ -1862,7 +1867,7 @@ Wallet.prototype._insert = co(function* insert(tx) { this.emit('address', derived); } - return result; + return details; }); /** @@ -1986,27 +1991,6 @@ Wallet.prototype.getLocked = function getLocked() { return this.txdb.getLocked(); }; -/** - * Map a transactions' addresses to wallet IDs. - * @param {TX} tx - * @returns {Promise} - Returns {@link PathInfo}. - */ - -Wallet.prototype.getPathInfo = co(function* getPathInfo(tx) { - var hashes = tx.getHashes('hex'); - var paths = []; - var i, hash, path; - - for (i = 0; i < hashes.length; i++) { - hash = hashes[i]; - path = yield this.getPath(hash); - if (path) - paths.push(path); - } - - return new PathInfo(this, tx, paths); -}); - /** * Get all transactions in transaction history (accesses db). * @param {(String|Number)?} acct