From 8346cccbcb5bbf85df9e840a2a638ceb3f792e12 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 31 Mar 2016 16:17:10 -0700 Subject: [PATCH] balance. http. --- lib/bcoin/http/client.js | 217 ++++++++++++++++++++++++++++++------- lib/bcoin/http/provider.js | 16 ++- lib/bcoin/http/server.js | 22 +++- lib/bcoin/txdb.js | 2 +- lib/bcoin/wallet.js | 11 +- lib/bcoin/walletdb.js | 88 ++++++++------- test/node-test.js | 10 +- test/wallet-test.js | 12 +- 8 files changed, 275 insertions(+), 103 deletions(-) diff --git a/lib/bcoin/http/client.js b/lib/bcoin/http/client.js index 2c9c2e74..91195ee6 100644 --- a/lib/bcoin/http/client.js +++ b/lib/bcoin/http/client.js @@ -13,13 +13,14 @@ var request = require('./request'); * Client */ -function Client(uri) { +function Client(uri, passphrase) { if (!(this instanceof Client)) return new Client(uri); EventEmitter.call(this); this.uri = uri; + this.passphrase = passphrase; this.loaded = false; this.id = null; this._init(); @@ -44,24 +45,46 @@ Client.prototype._init = function _init() { this.socket.on('open', function() { self.socket.on('tx', function(tx, map) { - self.emit('tx', bcoin.tx.fromJSON(tx), map); + try { + tx = bcoin.tx.fromJSON(tx); + } catch (e) { + return self.emit('error', e); + } + self.emit('tx', tx, map); }); self.socket.on('confirmed', function(tx, map) { - self.emit('confirmed', bcoin.tx.fromJSON(tx), map); + try { + tx = bcoin.tx.fromJSON(tx); + } catch (e) { + return self.emit('error', e); + } + self.emit('confirmed', tx, map); }); self.socket.on('updated', function(tx, map) { - self.emit('updated', bcoin.tx.fromJSON(tx), map); + try { + tx = bcoin.tx.fromJSON(tx); + } catch (e) { + return self.emit('error', e); + } + self.emit('updated', tx, map); }); self.socket.on('balance', function(balance, id) { - self.emit('balance', utils.satoshi(balance), id); + self.emit('balance', { + confirmed: utils.satoshi(balance.confirmed), + unconfirmed: utils.satoshi(balance.unconfirmed) + }, id); }); - self.socket.on('balances', function(balances) { - Object.keys(balances).forEach(function(id) { - balances[id] = utils.satoshi(balances[id]); + self.socket.on('balances', function(json) { + var balances = {}; + Object.keys(json).forEach(function(id) { + balances[id] = { + confirmed: utils.satoshi(json[id].confirmed), + unconfirmed: utils.satoshi(json[id].unconfirmed) + }; }); self.emit('balances', balances); }); @@ -98,9 +121,17 @@ Client.prototype.unlistenAll = function unlistenAll() { this.unlistenWallet('!all'); }; -Client.prototype.destroy = function destroy() { +Client.prototype.close = +Client.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); + + if (!this.socket) + return utils.nextTick(callback); + this.socket.destroy(); this.socket = null; + + return utils.nextTick(callback); }; Client.prototype._request = function _request(method, endpoint, json, callback) { @@ -111,6 +142,9 @@ Client.prototype._request = function _request(method, endpoint, json, callback) json = null; } + if (json) + json.passphrase = this.passphrase; + if (json && method === 'get') { json = null; query = json; @@ -167,9 +201,15 @@ Client.prototype.getWalletAll = function getWalletAll(id, callback) { if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.tx.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.tx.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -181,9 +221,15 @@ Client.prototype.getWalletCoins = function getWalletCoins(id, callback) { if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.coin.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.coin.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -195,9 +241,15 @@ Client.prototype.getWalletPending = function getPending(id, callback) { if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.coin.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.coin.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -209,7 +261,10 @@ Client.prototype.getWalletBalance = function getBalance(id, callback) { if (!body) return callback(new Error('Not found.')); - return callback(null, utils.satoshi(body.balance)); + return callback(null, { + confirmed: utils.satoshi(body.balance.confirmed), + unconfirmed: utils.satoshi(body.balance.unconfirmed) + }); }); }; @@ -222,9 +277,15 @@ Client.prototype.getWalletLast = function getLast(id, limit, callback) { if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.tx.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.tx.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -236,9 +297,15 @@ Client.prototype.getWalletRange = function getWalletRange(id, options, callback) if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.tx.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.tx.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -252,21 +319,36 @@ Client.prototype.getWalletTX = function getTX(id, hash, callback) { if (!body) return callback(null, []); - return callback(null, bcoin.tx.fromJSON(body)); + try { + body = bcoin.tx.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; Client.prototype.getWalletCoin = function getCoin(id, hash, index, callback) { - hash = utils.revHex(hash); + var path; - return this._get('/wallet/' + id + '/coin/' + hash + '/' + index, function(err, body) { + hash = utils.revHex(hash); + path = '/wallet/' + id + '/coin/' + hash + '/' + index; + + return this._get(path, function(err, body) { if (err) return callback(err); if (!body) return callback(null, []); - return callback(null, bcoin.coin.fromJSON(body)); + try { + body = bcoin.coin.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -293,9 +375,15 @@ Client.prototype.getCoinsByAddress = function getCoinsByAddress(address, callbac if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.coin.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.coin.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -307,9 +395,15 @@ Client.prototype.getCoin = function getCoin(hash, index, callback) { return callback(err); if (!body) - return callback(null, []); + return callback(); - return callback(null, bcoin.coin.fromJSON(body)); + try { + body = bcoin.coin.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -323,9 +417,15 @@ Client.prototype.getTXByAddress = function getTXByAddress(address, callback) { if (!body) return callback(null, []); - return callback(null, body.map(function(data) { - return bcoin.tx.fromJSON(data); - })); + try { + body = body.map(function(data) { + return bcoin.tx.fromJSON(data); + }); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -337,9 +437,15 @@ Client.prototype.getTX = function getTX(hash, callback) { return callback(err); if (!body) - return callback(null, []); + return callback(); - return callback(null, bcoin.tx.fromJSON(body)); + try { + body = bcoin.tx.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -352,9 +458,15 @@ Client.prototype.getBlock = function getBlock(hash, callback) { return callback(err); if (!body) - return callback(null, []); + return callback(); - return callback(null, bcoin.block.fromJSON(body)); + try { + body = bcoin.block.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); }); }; @@ -370,6 +482,29 @@ Client.prototype.broadcast = function broadcast(tx, callback) { }); }; +Client.prototype.walletSend = function walletSend(id, options, callback) { + var body = { + address: options.address, + value: utils.btc(options.value) + }; + + callback = utils.ensure(callback); + + return this._post('/wallet/' + id + '/send', body, function(err, body) { + if (err) + return callback(err); + + try { + body = bcoin.tx.fromJSON(body); + } catch (e) { + return callback(e); + } + + return callback(null, body); + }); +}; + + /** * Expose */ diff --git a/lib/bcoin/http/provider.js b/lib/bcoin/http/provider.js index e04833f0..0be54103 100644 --- a/lib/bcoin/http/provider.js +++ b/lib/bcoin/http/provider.js @@ -53,13 +53,23 @@ Provider.prototype._init = function _init() { }; Provider.prototype.setID = function setID(id) { - assert(!this.id) + assert(!this.id, 'ID has already been set.'); this.id = id; this.client.listenWallet(id); }; -Provider.prototype.destroy = function destroy() { - this.client.destroy(); +Provider.prototype.open = function open(callback) { + this.client.open(callback); +}; + +Provider.prototype.close = +Provider.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); + + if (!this.client) + return utils.nextTick(callback); + + this.client.destroy(callback); this.client = null; }; diff --git a/lib/bcoin/http/server.js b/lib/bcoin/http/server.js index 3713f596..7b57964b 100644 --- a/lib/bcoin/http/server.js +++ b/lib/bcoin/http/server.js @@ -299,7 +299,10 @@ NodeServer.prototype._init = function _init() { if (!balance) return send(404); - send(200, { balance: utils.btc(balance) }); + send(200, { + confirmed: utils.btc(balance.confirmed), + unconfirmed: utils.btc(balance.unconfirmed) + }); }); }); @@ -464,16 +467,23 @@ NodeServer.prototype._initIO = function _initIO() { }); this.walletdb.on('balance', function(balance, id) { - balance = utils.btc(balance); - self.server.io.to(id).emit('balance', balance); - self.server.io.to('!all').emit('balance', balance, id); + var json = { + confirmed: utils.btc(balance.confirmed), + unconfirmed: utils.btc(balance.unconfirmed) + }; + self.server.io.to(id).emit('balance', json); + self.server.io.to('!all').emit('balance', json, id); }); this.walletdb.on('balances', function(balances) { + var json = {}; Object.keys(balances).forEach(function(id) { - balances[id] = utils.btc(balances[id]); + json[id] = { + confirmed: utils.btc(balances[id].confirmed), + unconfirmed: utils.btc(balances[id].unconfirmed) + }; }); - self.server.io.to('!all').emit('balances', balances); + self.server.io.to('!all').emit('balances', json); }); }; diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index d37ff92b..20a843b0 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -1579,7 +1579,7 @@ TXPool.prototype.getBalance = function getBalance(address, callback) { unconfirmed.iadd(coins[i].value); } - return callback(null, unconfirmed, confirmed, coins); + return callback(null, { confirmed: confirmed, unconfirmed: unconfirmed }); }); }; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 6c2e97f8..d91d6ff5 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -168,11 +168,14 @@ Wallet.prototype.open = function open(callback) { this.once('open', callback); }; -Wallet.prototype.destroy = function destroy() { - if (!this.provider) - return; +Wallet.prototype.close = +Wallet.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); - this.provider.destroy(); + if (!this.provider) + return utils.nextTick(callback); + + this.provider.destroy(callback); this.provider = null; }; diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 05a6b32b..1805f45e 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -133,6 +133,9 @@ WalletDB.prototype._init = function _init() { var balances = {}; self.emit('updated', tx, map); + map.all.forEach(function(id) { + self.emit(id + ' updated', tx); + }); utils.forEachSerial(map.output, function(id, next) { if (self.listeners('balance').length === 0 @@ -142,29 +145,32 @@ WalletDB.prototype._init = function _init() { self.getBalance(id, function(err, balance) { if (err) - return self.emit('error', err); + return next(err); balances[id] = balance; self.emit('balance', balance, id); self.emit(id + ' balance', balance); + + next(); }); }, function(err) { if (err) - self.emit('error', err); + return self.emit('error', err); - self.emit('balances', balances, map); - }); + // Only sync for confirmed txs. + if (tx.ts === 0) { + self.emit('balances', balances, map); + return; + } - // Only sync for confirmed txs. - if (tx.ts === 0) - return; - - utils.forEachSerial(map.output, function(id, next) { - self.syncOutputDepth(id, tx, next); - }, function(err) { - if (err) - self.emit('error', err); + utils.forEachSerial(map.output, function(id, next) { + self.syncOutputDepth(id, tx, next); + }, function(err) { + if (err) + self.emit('error', err); + self.emit('balances', balances, map); + }); }); }); }; @@ -443,8 +449,10 @@ WalletDB.prototype.create = function create(options, callback) { } done(); } else { - if (bcoin.protocol.network.witness) + if (bcoin.protocol.network.type === 'segnet3' + || bcoin.protocol.network.type === 'segnet4') { options.witness = options.witness !== false; + } options.provider = new Provider(self); wallet = new bcoin.wallet(options); @@ -618,7 +626,7 @@ Provider.prototype.open = function open(callback) { Provider.prototype.setID = function setID(id) { var self = this; - assert(!this.id); + assert(!this.id, 'ID has already been set.'); this.id = id; @@ -643,35 +651,41 @@ Provider.prototype.setID = function setID(id) { }); }; -Provider.prototype.destroy = function destroy() { - if (this.db) { - if (this._onTX) { - this.removeListener(this.id + ' tx', this._onTX); - delete this._onTX; - } +Provider.prototype.close = +Provider.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); - if (this._onUpdated) { - this.removeListener(this.id + ' updated', this._onUpdated); - delete this._onUpdated; - } + if (!this.db) + return utils.nextTick(callback); - if (this._onConfirmed) { - this.removeListener(this.id + ' confirmed', this._onConfirmed); - delete this._onConfirmed; - } + if (this._onTX) { + this.db.removeListener(this.id + ' tx', this._onTX); + delete this._onTX; + } - if (this._onUnconfirmed) { - this.removeListener(this.id + ' unconfirmed', this._onUnconfirmed); - delete this._onUnconfirmed; - } + if (this._onUpdated) { + this.db.removeListener(this.id + ' updated', this._onUpdated); + delete this._onUpdated; + } - if (this._onBalance) { - this.removeListener(this.id + ' balance', this._onBalance); - delete this._onBalance; - } + if (this._onConfirmed) { + this.db.removeListener(this.id + ' confirmed', this._onConfirmed); + delete this._onConfirmed; + } + + if (this._onUnconfirmed) { + this.db.removeListener(this.id + ' unconfirmed', this._onUnconfirmed); + delete this._onUnconfirmed; + } + + if (this._onBalance) { + this.db.removeListener(this.id + ' balance', this._onBalance); + delete this._onBalance; } this.db = null; + + return utils.nextTick(callback); }; Provider.prototype.getAll = function getAll(callback) { diff --git a/test/node-test.js b/test/node-test.js index 8d5afc38..6813bbe8 100644 --- a/test/node-test.js +++ b/test/node-test.js @@ -101,27 +101,27 @@ describe('Wallet', function() { assert.noError(err); node.mempool.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '0'); + assert.equal(balance.unconfirmed.toString(10), '0'); node.mempool.addTX(t1, function(err) { assert.noError(err); node.mempool.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '60000'); + assert.equal(balance.unconfirmed.toString(10), '60000'); node.mempool.addTX(t2, function(err) { assert.noError(err); node.mempool.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '50000'); + assert.equal(balance.unconfirmed.toString(10), '50000'); node.mempool.addTX(t3, function(err) { assert.noError(err); node.mempool.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '22000'); + assert.equal(balance.unconfirmed.toString(10), '22000'); node.mempool.addTX(f1, function(err) { assert.noError(err); node.mempool.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '20000'); + assert.equal(balance.unconfirmed.toString(10), '20000'); node.mempool.getAll(function(err, txs) { assert(txs.some(function(tx) { return tx.hash('hex') === f1.hash('hex'); diff --git a/test/wallet-test.js b/test/wallet-test.js index 6fa137fa..2edd4129 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -190,26 +190,26 @@ describe('Wallet', function() { assert.noError(err); w.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '22500'); + assert.equal(balance.unconfirmed.toString(10), '22500'); wdb.addTX(t1, function(err) { w.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '73000'); + assert.equal(balance.unconfirmed.toString(10), '73000'); wdb.addTX(t2, function(err) { assert.noError(err); w.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '47000'); + assert.equal(balance.unconfirmed.toString(10), '47000'); wdb.addTX(t3, function(err) { assert.noError(err); w.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '22000'); + assert.equal(balance.unconfirmed.toString(10), '22000'); wdb.addTX(f1, function(err) { assert.noError(err); w.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '11000'); + assert.equal(balance.unconfirmed.toString(10), '11000'); w.getAll(function(err, txs) { assert(txs.some(function(tx) { return tx.hash('hex') === f1.hash('hex'); @@ -244,7 +244,7 @@ describe('Wallet', function() { assert.noError(err); dw.getBalance(function(err, balance) { assert.noError(err); - assert.equal(balance.toString(10), '11000'); + assert.equal(balance.unconfirmed.toString(10), '11000'); cb(); }); });