balance. http.

This commit is contained in:
Christopher Jeffrey 2016-03-31 16:17:10 -07:00
parent b0e6826232
commit 8346cccbcb
8 changed files with 275 additions and 103 deletions

View File

@ -13,13 +13,14 @@ var request = require('./request');
* Client * Client
*/ */
function Client(uri) { function Client(uri, passphrase) {
if (!(this instanceof Client)) if (!(this instanceof Client))
return new Client(uri); return new Client(uri);
EventEmitter.call(this); EventEmitter.call(this);
this.uri = uri; this.uri = uri;
this.passphrase = passphrase;
this.loaded = false; this.loaded = false;
this.id = null; this.id = null;
this._init(); this._init();
@ -44,24 +45,46 @@ Client.prototype._init = function _init() {
this.socket.on('open', function() { this.socket.on('open', function() {
self.socket.on('tx', function(tx, map) { 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.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.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.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) { self.socket.on('balances', function(json) {
Object.keys(balances).forEach(function(id) { var balances = {};
balances[id] = utils.satoshi(balances[id]); Object.keys(json).forEach(function(id) {
balances[id] = {
confirmed: utils.satoshi(json[id].confirmed),
unconfirmed: utils.satoshi(json[id].unconfirmed)
};
}); });
self.emit('balances', balances); self.emit('balances', balances);
}); });
@ -98,9 +121,17 @@ Client.prototype.unlistenAll = function unlistenAll() {
this.unlistenWallet('!all'); 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.destroy();
this.socket = null; this.socket = null;
return utils.nextTick(callback);
}; };
Client.prototype._request = function _request(method, endpoint, json, 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; json = null;
} }
if (json)
json.passphrase = this.passphrase;
if (json && method === 'get') { if (json && method === 'get') {
json = null; json = null;
query = json; query = json;
@ -167,9 +201,15 @@ Client.prototype.getWalletAll = function getWalletAll(id, callback) {
if (!body) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.tx.fromJSON(data); 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.coin.fromJSON(data); 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.coin.fromJSON(data); 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) if (!body)
return callback(new Error('Not found.')); 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.tx.fromJSON(data); 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.tx.fromJSON(data); 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) if (!body)
return callback(null, []); 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) { 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) if (err)
return callback(err); return callback(err);
if (!body) if (!body)
return callback(null, []); 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.coin.fromJSON(data); 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); return callback(err);
if (!body) 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) if (!body)
return callback(null, []); return callback(null, []);
return callback(null, body.map(function(data) { try {
return bcoin.tx.fromJSON(data); 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); return callback(err);
if (!body) 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); return callback(err);
if (!body) 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 * Expose
*/ */

View File

@ -53,13 +53,23 @@ Provider.prototype._init = function _init() {
}; };
Provider.prototype.setID = function setID(id) { Provider.prototype.setID = function setID(id) {
assert(!this.id) assert(!this.id, 'ID has already been set.');
this.id = id; this.id = id;
this.client.listenWallet(id); this.client.listenWallet(id);
}; };
Provider.prototype.destroy = function destroy() { Provider.prototype.open = function open(callback) {
this.client.destroy(); 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; this.client = null;
}; };

View File

@ -299,7 +299,10 @@ NodeServer.prototype._init = function _init() {
if (!balance) if (!balance)
return send(404); 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) { this.walletdb.on('balance', function(balance, id) {
balance = utils.btc(balance); var json = {
self.server.io.to(id).emit('balance', balance); confirmed: utils.btc(balance.confirmed),
self.server.io.to('!all').emit('balance', balance, id); 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) { this.walletdb.on('balances', function(balances) {
var json = {};
Object.keys(balances).forEach(function(id) { 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);
}); });
}; };

View File

@ -1579,7 +1579,7 @@ TXPool.prototype.getBalance = function getBalance(address, callback) {
unconfirmed.iadd(coins[i].value); unconfirmed.iadd(coins[i].value);
} }
return callback(null, unconfirmed, confirmed, coins); return callback(null, { confirmed: confirmed, unconfirmed: unconfirmed });
}); });
}; };

View File

@ -168,11 +168,14 @@ Wallet.prototype.open = function open(callback) {
this.once('open', callback); this.once('open', callback);
}; };
Wallet.prototype.destroy = function destroy() { Wallet.prototype.close =
if (!this.provider) Wallet.prototype.destroy = function destroy(callback) {
return; callback = utils.ensure(callback);
this.provider.destroy(); if (!this.provider)
return utils.nextTick(callback);
this.provider.destroy(callback);
this.provider = null; this.provider = null;
}; };

View File

@ -133,6 +133,9 @@ WalletDB.prototype._init = function _init() {
var balances = {}; var balances = {};
self.emit('updated', tx, map); self.emit('updated', tx, map);
map.all.forEach(function(id) {
self.emit(id + ' updated', tx);
});
utils.forEachSerial(map.output, function(id, next) { utils.forEachSerial(map.output, function(id, next) {
if (self.listeners('balance').length === 0 if (self.listeners('balance').length === 0
@ -142,29 +145,32 @@ WalletDB.prototype._init = function _init() {
self.getBalance(id, function(err, balance) { self.getBalance(id, function(err, balance) {
if (err) if (err)
return self.emit('error', err); return next(err);
balances[id] = balance; balances[id] = balance;
self.emit('balance', balance, id); self.emit('balance', balance, id);
self.emit(id + ' balance', balance); self.emit(id + ' balance', balance);
next();
}); });
}, function(err) { }, function(err) {
if (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. utils.forEachSerial(map.output, function(id, next) {
if (tx.ts === 0) self.syncOutputDepth(id, tx, next);
return; }, function(err) {
if (err)
utils.forEachSerial(map.output, function(id, next) { self.emit('error', err);
self.syncOutputDepth(id, tx, next); self.emit('balances', balances, map);
}, function(err) { });
if (err)
self.emit('error', err);
}); });
}); });
}; };
@ -443,8 +449,10 @@ WalletDB.prototype.create = function create(options, callback) {
} }
done(); done();
} else { } else {
if (bcoin.protocol.network.witness) if (bcoin.protocol.network.type === 'segnet3'
|| bcoin.protocol.network.type === 'segnet4') {
options.witness = options.witness !== false; options.witness = options.witness !== false;
}
options.provider = new Provider(self); options.provider = new Provider(self);
wallet = new bcoin.wallet(options); wallet = new bcoin.wallet(options);
@ -618,7 +626,7 @@ Provider.prototype.open = function open(callback) {
Provider.prototype.setID = function setID(id) { Provider.prototype.setID = function setID(id) {
var self = this; var self = this;
assert(!this.id); assert(!this.id, 'ID has already been set.');
this.id = id; this.id = id;
@ -643,35 +651,41 @@ Provider.prototype.setID = function setID(id) {
}); });
}; };
Provider.prototype.destroy = function destroy() { Provider.prototype.close =
if (this.db) { Provider.prototype.destroy = function destroy(callback) {
if (this._onTX) { callback = utils.ensure(callback);
this.removeListener(this.id + ' tx', this._onTX);
delete this._onTX;
}
if (this._onUpdated) { if (!this.db)
this.removeListener(this.id + ' updated', this._onUpdated); return utils.nextTick(callback);
delete this._onUpdated;
}
if (this._onConfirmed) { if (this._onTX) {
this.removeListener(this.id + ' confirmed', this._onConfirmed); this.db.removeListener(this.id + ' tx', this._onTX);
delete this._onConfirmed; delete this._onTX;
} }
if (this._onUnconfirmed) { if (this._onUpdated) {
this.removeListener(this.id + ' unconfirmed', this._onUnconfirmed); this.db.removeListener(this.id + ' updated', this._onUpdated);
delete this._onUnconfirmed; delete this._onUpdated;
} }
if (this._onBalance) { if (this._onConfirmed) {
this.removeListener(this.id + ' balance', this._onBalance); this.db.removeListener(this.id + ' confirmed', this._onConfirmed);
delete this._onBalance; 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; this.db = null;
return utils.nextTick(callback);
}; };
Provider.prototype.getAll = function getAll(callback) { Provider.prototype.getAll = function getAll(callback) {

View File

@ -101,27 +101,27 @@ describe('Wallet', function() {
assert.noError(err); assert.noError(err);
node.mempool.getBalance(function(err, balance) { node.mempool.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '0'); assert.equal(balance.unconfirmed.toString(10), '0');
node.mempool.addTX(t1, function(err) { node.mempool.addTX(t1, function(err) {
assert.noError(err); assert.noError(err);
node.mempool.getBalance(function(err, balance) { node.mempool.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '60000'); assert.equal(balance.unconfirmed.toString(10), '60000');
node.mempool.addTX(t2, function(err) { node.mempool.addTX(t2, function(err) {
assert.noError(err); assert.noError(err);
node.mempool.getBalance(function(err, balance) { node.mempool.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '50000'); assert.equal(balance.unconfirmed.toString(10), '50000');
node.mempool.addTX(t3, function(err) { node.mempool.addTX(t3, function(err) {
assert.noError(err); assert.noError(err);
node.mempool.getBalance(function(err, balance) { node.mempool.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '22000'); assert.equal(balance.unconfirmed.toString(10), '22000');
node.mempool.addTX(f1, function(err) { node.mempool.addTX(f1, function(err) {
assert.noError(err); assert.noError(err);
node.mempool.getBalance(function(err, balance) { node.mempool.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '20000'); assert.equal(balance.unconfirmed.toString(10), '20000');
node.mempool.getAll(function(err, txs) { node.mempool.getAll(function(err, txs) {
assert(txs.some(function(tx) { assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex'); return tx.hash('hex') === f1.hash('hex');

View File

@ -190,26 +190,26 @@ describe('Wallet', function() {
assert.noError(err); assert.noError(err);
w.getBalance(function(err, balance) { w.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '22500'); assert.equal(balance.unconfirmed.toString(10), '22500');
wdb.addTX(t1, function(err) { wdb.addTX(t1, function(err) {
w.getBalance(function(err, balance) { w.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '73000'); assert.equal(balance.unconfirmed.toString(10), '73000');
wdb.addTX(t2, function(err) { wdb.addTX(t2, function(err) {
assert.noError(err); assert.noError(err);
w.getBalance(function(err, balance) { w.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '47000'); assert.equal(balance.unconfirmed.toString(10), '47000');
wdb.addTX(t3, function(err) { wdb.addTX(t3, function(err) {
assert.noError(err); assert.noError(err);
w.getBalance(function(err, balance) { w.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '22000'); assert.equal(balance.unconfirmed.toString(10), '22000');
wdb.addTX(f1, function(err) { wdb.addTX(f1, function(err) {
assert.noError(err); assert.noError(err);
w.getBalance(function(err, balance) { w.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '11000'); assert.equal(balance.unconfirmed.toString(10), '11000');
w.getAll(function(err, txs) { w.getAll(function(err, txs) {
assert(txs.some(function(tx) { assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex'); return tx.hash('hex') === f1.hash('hex');
@ -244,7 +244,7 @@ describe('Wallet', function() {
assert.noError(err); assert.noError(err);
dw.getBalance(function(err, balance) { dw.getBalance(function(err, balance) {
assert.noError(err); assert.noError(err);
assert.equal(balance.toString(10), '11000'); assert.equal(balance.unconfirmed.toString(10), '11000');
cb(); cb();
}); });
}); });