diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index e43f97cf..66d746ef 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -482,7 +482,7 @@ Wallet.prototype.retoken = co(function* retoken(passphrase) { */ Wallet.prototype._retoken = co(function* retoken(passphrase) { - var master = yield this.unlock(passphrase); + yield this.unlock(passphrase); this.tokenDepth++; this.token = this.getToken(this.tokenDepth); @@ -1483,6 +1483,99 @@ Wallet.prototype._send = co(function* send(options, passphrase) { return tx; }); +/** + * Intentionally double-spend outputs by + * increasing fee for an existing transaction. + * @param {Hash} hash + * @param {Rate} rate + * @param {(String|Buffer)?} passphrase + * @returns {Promise} - Returns {@link TX}. + */ + +Wallet.prototype.increaseFee = co(function* increaseFee(hash, rate, passphrase) { + var tx = yield this.getTX(hash); + var i, oldFee, fee, path, input, output, change; + + if (!tx) + throw new Error('Transaction not found.'); + + if (tx.isCoinbase()) + throw new Error('Transaction is a coinbase.'); + + yield this.fillHistory(tx); + + if (!tx.hasCoins()) + throw new Error('Not all coins available.'); + + if (!utils.isUInt32(rate)) + throw new Error('Rate must be a number.'); + + oldFee = tx.getFee(); + fee = tx.getMinFee(null, rate); + + if (fee > constants.tx.MAX_FEE) + fee = constants.tx.MAX_FEE; + + if (oldFee >= fee) + throw new Error('Fee is not increasing.'); + + tx = MTX.fromRaw(tx.toRaw()); + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + input.script.length = 0; + input.script.compile(); + input.witness.length = 0; + input.witness.compile(); + } + + for (i = 0; i < tx.outputs.length; i++) { + output = tx.outputs[i]; + path = yield this.getPath(output.getAddress()); + + if (!path) + continue; + + if (path.branch === 1) { + change = output; + tx.changeIndex = i; + break; + } + } + + if (!change) + throw new Error('No change output.'); + + change.value += oldFee; + + if (tx.getFee() !== 0) + throw new Error('Arithmetic error for change.'); + + change.value -= fee; + + if (change.value < 0) + throw new Error('Fee is too high.'); + + if (change.isDust(constants.tx.MIN_RELAY)) { + tx.outputs.splice(tx.changeIndex, 1); + tx.changeIndex = -1; + } + + yield this.sign(tx, passphrase); + + if (!tx.isSigned()) + throw new Error('TX could not be fully signed.'); + + tx = tx.toTX(); + + this.logger.debug('Increasing fee for wallet tx (%s): %s', this.id, tx.rhash); + + yield this.db.addTX(tx); + yield this.db.send(tx); + + return tx; +}); + /** * Resend pending wallet transactions. * @returns {Promise} diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 9f81527b..9f9dee5a 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -1724,7 +1724,7 @@ WalletDB.prototype.rollback = co(function* rollback(height) { */ WalletDB.prototype.revert = co(function* revert(height) { - var i, iter, item, height, block, tx; + var i, iter, item, block, tx; iter = this.db.iterator({ gte: layout.b(height + 1), @@ -1739,7 +1739,6 @@ WalletDB.prototype.revert = co(function* revert(height) { break; try { - height = layout.bb(item.key); block = BlockMapRecord.fromRaw(item.value); for (i = block.txs.length - 1; i >= 0; i--) {