diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index fd2e1651..1247c26a 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -241,6 +241,13 @@ Fullnode.prototype._init = function _init() { this.miner.on('block', function(block) { self.pool.broadcast(block.toInv()); }); + + this.walletdb.on('send', function(tx) { + self.sendTX(tx, function(err) { + if (err) + self.emit('error', err); + }); + }); }; /** diff --git a/lib/bcoin/http/rpc.js b/lib/bcoin/http/rpc.js index 4c5eb105..784bb2b4 100644 --- a/lib/bcoin/http/rpc.js +++ b/lib/bcoin/http/rpc.js @@ -2560,21 +2560,7 @@ RPC.prototype._send = function _send(account, address, amount, subtractFee, call }] }; - this.wallet.createTX(options, function(err, tx) { - if (err) - return callback(err); - - self.wallet.sign(tx, function(err) { - if (err) - return callback(err); - - self.node.sendTX(tx, function(err) { - if (err) - return callback(err); - callback(null, tx); - }); - }); - }); + this.wallet.send(options, callback); }; RPC.prototype.sendfrom = function sendfrom(args, callback) { diff --git a/lib/bcoin/http/server.js b/lib/bcoin/http/server.js index 8e11bd02..cec1d7f4 100644 --- a/lib/bcoin/http/server.js +++ b/lib/bcoin/http/server.js @@ -597,21 +597,11 @@ HTTPServer.prototype._init = function _init() { var id = req.options.id; var options = req.options; - self.walletdb.createTX(id, options, function(err, tx) { + self.walletdb.send(id, options, function(err, tx) { if (err) return next(err); - self.walletdb.sign(id, tx, options, function(err) { - if (err) - return next(err); - - self.node.sendTX(tx, function(err) { - if (err) - return next(err); - - send(200, tx.toJSON()); - }); - }); + send(200, tx.toJSON()); }); }); diff --git a/lib/bcoin/spvnode.js b/lib/bcoin/spvnode.js index d9eaf10e..5e8493a2 100644 --- a/lib/bcoin/spvnode.js +++ b/lib/bcoin/spvnode.js @@ -143,6 +143,13 @@ SPVNode.prototype._init = function _init() { this.walletdb.on('save address', function(address) { self.pool.watch(address.getHash()); }); + + this.walletdb.on('send', function(tx) { + self.sendTX(tx, function(err) { + if (err) + self.emit('error', err); + }); + }); }; /** diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index a0ac0d8d..b07f3f28 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -52,7 +52,7 @@ function Wallet(db, options) { this.network = db.network; this.workerPool = db.workerPool; this.writeLock = new bcoin.locker(this); - this.fillLock = new bcoin.locker(this); + this.fundLock = new bcoin.locker(this); this.id = null; this.master = null; @@ -687,7 +687,7 @@ Wallet.prototype.getPath = function getPath(address, callback) { * fee from existing outputs rather than adding more inputs. */ -Wallet.prototype.fund = function fund(tx, options, callback) { +Wallet.prototype.fund = function fund(tx, options, callback, force) { var self = this; var unlock, rate; @@ -701,7 +701,7 @@ Wallet.prototype.fund = function fund(tx, options, callback) { // We use a lock here to ensure we // don't end up double spending coins. - unlock = this.fillLock.lock(fund, [tx, options, callback]); + unlock = this.fundLock.lock(fund, [tx, options, callback], force); if (!unlock) return; @@ -764,13 +764,13 @@ Wallet.prototype.fund = function fund(tx, options, callback) { /** * Build a transaction, fill it with outputs and inputs, * sort the members according to BIP69, set locktime, - * and sign it (accesses db). + * and template it. * @param {Object} options - See {@link Wallet#fund options}. * @param {Object[]} options.outputs - See {@link MTX#addOutput}. * @param {Function} callback - Returns [Error, {@link MTX}]. */ -Wallet.prototype.createTX = function createTX(options, callback) { +Wallet.prototype.createTX = function createTX(options, callback, force) { var self = this; var outputs = options.outputs; var i, tx; @@ -821,7 +821,51 @@ Wallet.prototype.createTX = function createTX(options, callback) { return callback(null, tx); }); - }); + }, force); +}; + +/** + * Build a transaction, fill it with outputs and inputs, + * sort the members according to BIP69, set locktime, + * sign and broadcast. Doing this all in one go prevents + * coins from being double spent. + * @param {Object} options - See {@link Wallet#fund options}. + * @param {Object[]} options.outputs - See {@link MTX#addOutput}. + * @param {Function} callback - Returns [Error, {@link TX}]. + */ + +Wallet.prototype.send = function send(options, callback) { + var self = this; + var unlock = this.fundLock.lock(send, [options, callback]); + + if (!unlock) + return; + + callback = utils.wrap(callback, unlock); + + this.createTX(options, function(err, tx) { + if (err) + return callback(err); + + self.sign(tx, function(err) { + if (err) + return callback(err); + + if (!tx.isSigned()) + return callback(new Error('TX could not be fully signed.')); + + tx = tx.toTX(); + + self.addTX(tx, function(err) { + if (err) + return callback(err); + + self.db.emit('send', tx); + + return callback(null, tx); + }); + }); + }, true); }; /** diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index d578e743..477f272d 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -1407,6 +1407,12 @@ WalletDB.prototype.createTX = function createTX(id, options, callback) { }); }; +WalletDB.prototype.send = function send(id, options, callback) { + this.fetchWallet(id, callback, function(wallet, callback) { + wallet.send(options, callback); + }); +}; + WalletDB.prototype.addKey = function addKey(id, name, key, callback) { this.fetchWallet(id, callback, function(wallet, callback) { wallet.addKey(name, key, callback);