diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index ce76c119..db1960c1 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -255,6 +255,7 @@ function TXDB(wallet) { this.logger = wallet.db.logger; this.network = wallet.db.network; this.options = wallet.db.options; + this.locked = {}; this.locker = new bcoin.locker(this); this.coinCache = new bcoin.lru(10000, 1); @@ -808,6 +809,9 @@ TXDB.prototype.add = function add(tx, info, callback) { if (err) return callback(err); + // Clear any locked coins to free up memory. + self.unlockTX(tx); + self.emit('tx', tx, info); if (tx.ts !== 0) @@ -1000,6 +1004,9 @@ TXDB.prototype._confirm = function _confirm(tx, info, callback) { // and remove pending flag to mark as confirmed. assert(tx.height >= 0); + // Clear any locked coins to free up memory. + self.unlockTX(tx); + // Save the original received time. tx.ps = existing.ps; @@ -1308,6 +1315,90 @@ TXDB.prototype._unconfirm = function unconfirm(tx, info, callback, force) { }); }; +/** + * Lock all coins in a transaction. + * @param {TX} tx + */ + +TXDB.prototype.lockTX = function lockTX(tx) { + var i, input; + + if (tx.isCoinbase()) + return; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + this.lockCoin(input.prevout); + } +}; + +/** + * Unlock all coins in a transaction. + * @param {TX} tx + */ + +TXDB.prototype.unlockTX = function unlockTX(tx) { + var i, input; + + if (tx.isCoinbase()) + return; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + this.unlockCoin(input.prevout); + } +}; + +/** + * Lock a single coin. + * @param {Coin|Outpoint} coin + */ + +TXDB.prototype.lockCoin = function lockCoin(coin) { + var key = coin.hash + coin.index; + this.locked[key] = true; +}; + +/** + * Unlock a single coin. + * @param {Coin|Outpoint} coin + */ + +TXDB.prototype.unlockCoin = function unlockCoin(coin) { + var key = coin.hash + coin.index; + delete this.locked[key]; +}; + +/** + * Test locked status of a single coin. + * @param {Coin|Outpoint} coin + */ + +TXDB.prototype.isLocked = function isLocked(coin) { + var key = coin.hash + coin.index; + return this.locked[key] === true; +}; + +/** + * Filter array of coins or outpoints + * for only unlocked ones. + * @param {Coin[]|Outpoint[]} + * @returns {Array} + */ + +TXDB.prototype.filterLocked = function filterLocked(coins) { + var out = []; + var i, coin; + + for (i = 0; i < coins.length; i++) { + coin = coins[i]; + if (!this.isLocked(coin)) + out.push(coins); + } + + return coins; +}; + /** * Get hashes of all transactions in the database. * @param {Number?} account diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 13e6e332..cec5e666 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -815,6 +815,9 @@ Wallet.prototype.fund = function fund(tx, options, callback, force) { rate = self.network.getRate(); } + // Don't use any locked coins. + coins = self.tx.filterLocked(coins); + try { tx.fund(coins, { selection: options.selection || 'age', @@ -2053,8 +2056,6 @@ function Account(db, options) { if (!(this instanceof Account)) return new Account(db, options); - EventEmitter.call(this); - assert(db, 'Database is required.'); this.db = db; @@ -2082,8 +2083,6 @@ function Account(db, options) { this.fromOptions(options); } -utils.inherits(Account, EventEmitter); - /** * Inject properties from options object. * @private