wallet: drop wallet map in favor of path info and tx details.

This commit is contained in:
Christopher Jeffrey 2016-08-11 01:32:49 -07:00
parent 914bc8435d
commit abd267b7d2
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 356 additions and 760 deletions

View File

@ -2909,37 +2909,36 @@ RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) {
var self = this; var self = this;
var receive, member, json; var receive, member, json;
this.walletdb.tx.getMap(tx, function(err, map) { this.walletdb.tx.toDetails(this.wallet.id, tx, function(err, details) {
if (err) if (err)
return callback(err); return callback(err);
if (!map) if (!details)
return callback(new RPCError('TX not found.')); return callback(new RPCError('TX not found.'));
receive = map.inputs.length === 0; receive = details.isReceive();
member = receive ? map.outputs[0] : map.inputs[0]; member = details.getMember();
assert(member);
json = { json = {
amount: +utils.btc(tx.getOutputValue()), amount: +utils.btc(details.getValue()),
confirmations: tx.getConfirmations(self.chain.height), confirmations: details.confirmations,
blockhash: tx.block ? utils.revHex(tx.block) : null, blockhash: details.block ? utils.revHex(details.block) : null,
blockindex: tx.index, blockindex: details.index,
blocktime: tx.ts, blocktime: details.ts,
txid: tx.rhash, txid: utils.revHex(details.hash),
walletconflicts: [], walletconflicts: [],
time: tx.ps, time: details.ps,
timereceived: tx.ps, timereceived: details.ps,
'bip125-replaceable': 'no', 'bip125-replaceable': 'no',
details: [{ details: [{
account: member.name, account: member.path.name,
address: member.paths[0].address.toBase58(self.network), address: member.address.toBase58(self.network),
category: receive ? 'receive' : 'send', category: receive ? 'receive' : 'send',
amount: +utils.btc(member.value), amount: +utils.btc(member.value),
label: member.name, label: member.path.name,
vout: 0 vout: 0
}], }],
hex: tx.toRaw().toString('hex') hex: details.tx.toRaw().toString('hex')
}; };
callback(null, json); callback(null, json);
@ -3199,32 +3198,31 @@ RPC.prototype._toListTX = function _toListTX(tx, callback) {
var self = this; var self = this;
var receive, member, json; var receive, member, json;
this.walletdb.tx.getMap(tx, function(err, map) { this.walletdb.tx.toDetails(this.wallet.id, tx, function(err, details) {
if (err) if (err)
return callback(err); return callback(err);
if (!map) if (!details)
return callback(new RPCError('TX not found.')); return callback(new RPCError('TX not found.'));
receive = map.inputs.length === 0; receive = details.isReceive();
member = receive ? map.outputs[0] : map.inputs[0]; member = details.getMember();
assert(member);
json = { json = {
account: member.name, account: member.path.name,
address: member.paths[0].address.toBase58(self.network), address: member.address.toBase58(self.network),
category: receive ? 'receive' : 'send', category: receive ? 'receive' : 'send',
amount: +utils.btc(member.value), amount: +utils.btc(member.value),
label: member.name, label: member.path.name,
vout: 0, vout: 0,
confirmations: tx.getConfirmations(self.chain.height), confirmations: details.confirmations,
blockhash: tx.block ? utils.revHex(tx.block) : null, blockhash: details.block ? utils.revHex(details.block) : null,
blockindex: tx.index, blockindex: details.index,
blocktime: tx.ts, blocktime: details.ts,
txid: tx.rhash, txid: utils.revHex(details.hash),
walletconflicts: [], walletconflicts: [],
time: tx.ps, time: details.ps,
timereceived: tx.ps, timereceived: details.ps,
'bip125-replaceable': 'no' 'bip125-replaceable': 'no'
}; };

View File

@ -968,49 +968,36 @@ HTTPServer.prototype._initIO = function _initIO() {
}); });
}); });
this.walletdb.on('tx', function(tx, map) { this.walletdb.on('tx', function(id, tx, details) {
var summary = map.toJSON(); details = details.toJSON();
tx = tx.toJSON(); self.server.io.to(id).emit('wallet tx', details);
map.getWallets().forEach(function(id) { self.server.io.to('!all').emit('wallet tx', id, details);
self.server.io.to(id).emit('wallet tx', tx, summary);
});
self.server.io.to('!all').emit('wallet tx', tx, summary);
}); });
this.walletdb.on('confirmed', function(tx, map) { this.walletdb.on('confirmed', function(id, tx, details) {
var summary = map.toJSON(); details = details.toJSON();
tx = tx.toJSON(); self.server.io.to(id).emit('wallet confirmed', details);
map.getWallets().forEach(function(id) { self.server.io.to('!all').emit('wallet confirmed', id, details);
self.server.io.to(id).emit('wallet confirmed', tx, summary);
});
self.server.io.to('!all').emit('wallet confirmed', tx, summary);
}); });
this.walletdb.on('updated', function(tx, map) { this.walletdb.on('updated', function(id, tx, details) {
var summary = map.toJSON(); details = details.toJSON();
tx = tx.toJSON(); self.server.io.to(id).emit('wallet updated', details);
map.getWallets().forEach(function(id) { self.server.io.to('!all').emit('wallet updated', id, details);
self.server.io.to(id).emit('wallet updated', tx, summary);
});
self.server.io.to('!all').emit('wallet updated', tx, summary);
}); });
this.walletdb.on('balances', function(balances) { this.walletdb.on('balance', function(id, balance, details) {
var json = {}; balance = {
Object.keys(balances).forEach(function(id) { confirmed: utils.btc(balance.confirmed),
json[id] = { unconfirmed: utils.btc(balance.unconfirmed),
confirmed: utils.btc(balances[id].confirmed), total: utils.btc(balance.total)
unconfirmed: utils.btc(balances[id].unconfirmed), };
total: utils.btc(balances[id].total) self.server.io.to(id).emit('wallet balance', balance);
}; self.server.io.to('!all').emit('wallet balance', id, balance);
self.server.io.to(id).emit('wallet balance', json[id], id);
self.server.io.to('!all').emit('wallet balance', json[id], id);
});
self.server.io.to('!all').emit('wallet balances', json);
}); });
this.walletdb.on('address', function(receive, change, map) { this.walletdb.on('address', function(id, receive, change, details) {
var summary = map.toJSON(); details = details.toJSON();
receive = receive.map(function(address) { receive = receive.map(function(address) {
return address.toJSON(); return address.toJSON();
@ -1020,11 +1007,8 @@ HTTPServer.prototype._initIO = function _initIO() {
return address.toJSON(); return address.toJSON();
}); });
map.getWallets().forEach(function(id) { self.server.io.to(id).emit('wallet address', receive, change, details);
self.server.io.to(id).emit('wallet address', receive, change, summary); self.server.io.to('!all').emit('wallet address', id, receive, change, details);
});
self.server.io.to('!all').emit('wallet address', receive, change, summary);
}); });
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1030,18 +1030,19 @@ Wallet.prototype.getOutputPaths = function getOutputPaths(tx, callback) {
* Sync address depths based on a transaction's outputs. * Sync address depths based on a transaction's outputs.
* This is used for deriving new addresses when * This is used for deriving new addresses when
* a confirmed transaction is seen. * a confirmed transaction is seen.
* @param {WalletMap} map * @param {WalletMap} info
* @param {Function} callback - Returns [Errr, Boolean] * @param {Function} callback - Returns [Errr, Boolean]
* (true if new addresses were allocated). * (true if new addresses were allocated).
*/ */
Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) { Wallet.prototype.syncOutputDepth = function syncOutputDepth(info, callback) {
var self = this; var self = this;
var change = []; var change = [];
var receive = []; var receive = [];
var accounts = {};
var i, path, unlock; var i, path, unlock;
unlock = this.writeLock.lock(syncOutputDepth, [map, callback]); unlock = this.writeLock.lock(syncOutputDepth, [info, callback]);
if (!unlock) if (!unlock)
return; return;
@ -1050,8 +1051,22 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) {
this.start(); this.start();
utils.forEachSerial(map.outputs, function(output, next) { for (i = 0; i < info.paths.length; i++) {
var paths = output.paths; path = info.paths[i];
if (path.id !== this.id)
continue;
if (!accounts[path.account])
accounts[path.account] = [];
accounts[path.account].push(path);
}
accounts = utils.values(accounts);
utils.forEachSerial(accounts, function(paths, next) {
var account = paths[0].account;
var receiveDepth = -1; var receiveDepth = -1;
var changeDepth = -1; var changeDepth = -1;
@ -1070,7 +1085,7 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) {
receiveDepth += 2; receiveDepth += 2;
changeDepth += 2; changeDepth += 2;
self.getAccount(output.account, function(err, account) { self.getAccount(account, function(err, account) {
if (err) if (err)
return next(err); return next(err);

View File

@ -101,35 +101,35 @@ WalletDB.prototype._init = function _init() {
}); });
} }
function handleEvent(event, tx, map) { function handleEvent(event, tx, info) {
var i, path; var i, id, details;
self.emit(event, tx, map); for (i = 0; i < info.wallets.length; i++) {
id = info.wallets[i];
for (i = 0; i < map.accounts.length; i++) { details = info.toDetails(id);
path = map.accounts[i]; self.emit(event, id, tx, details);
self.fire(path.id, event, tx, path.name); self.fire(id, event, tx, details);
} }
} }
this.tx.on('tx', function(tx, map) { this.tx.on('tx', function(tx, info) {
handleEvent('tx', tx, map); handleEvent('tx', tx, info);
}); });
this.tx.on('conflict', function(tx, map) { this.tx.on('conflict', function(tx, info) {
handleEvent('conflict', tx, map); handleEvent('conflict', tx, info);
}); });
this.tx.on('confirmed', function(tx, map) { this.tx.on('confirmed', function(tx, info) {
handleEvent('confirmed', tx, map); handleEvent('confirmed', tx, info);
}); });
this.tx.on('unconfirmed', function(tx, map) { this.tx.on('unconfirmed', function(tx, info) {
handleEvent('unconfirmed', tx, map); handleEvent('unconfirmed', tx, info);
}); });
this.tx.on('updated', function(tx, map) { this.tx.on('updated', function(tx, info) {
handleEvent('updated', tx, map); handleEvent('updated', tx, info);
}); });
}; };
@ -248,16 +248,15 @@ WalletDB.prototype.commit = function commit(id, callback) {
* Emit balance events after a tx is saved. * Emit balance events after a tx is saved.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {WalletMap} map * @param {WalletMap} info
* @param {Function} callback * @param {Function} callback
*/ */
WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) { WalletDB.prototype.updateBalances = function updateBalances(tx, info, callback) {
var self = this; var self = this;
var balances = {}; var details;
var i, id, keys;
utils.forEachSerial(map.getOutputWallets(), function(id, next) { utils.forEachSerial(info.wallets, function(id, next) {
if (self.listeners('balances').length === 0 if (self.listeners('balances').length === 0
&& !self.hasListener(id, 'balance')) { && !self.hasListener(id, 'balance')) {
return next(); return next();
@ -267,44 +266,34 @@ WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
if (err) if (err)
return next(err); return next(err);
balances[id] = balance; details = info.toDetails(id);
self.emit('balance', id, balance, details);
self.fire(id, 'balance', balance, details);
next(); next();
}); });
}, function(err) { }, callback);
if (err)
return callback(err);
keys = Object.keys(balances);
for (i = 0; i < keys.length; i++) {
id = keys[i];
self.fire(id, 'balance', balances[id]);
}
self.emit('balances', balances, map);
return callback(null, balances);
});
}; };
/** /**
* Derive new addresses after a tx is saved. * Derive new addresses after a tx is saved.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {WalletMap} map * @param {WalletMap} info
* @param {Function} callback * @param {Function} callback
*/ */
WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) { WalletDB.prototype.syncOutputs = function syncOutputs(tx, info, callback) {
var self = this; var self = this;
var details;
utils.forEachSerial(map.getOutputWallets(), function(id, next) { utils.forEachSerial(info.wallets, function(id, next) {
self.syncOutputDepth(id, map, function(err, receive, change) { self.syncOutputDepth(id, info, function(err, receive, change) {
if (err) if (err)
return next(err); return next(err);
self.fire(id, 'address', receive, change); details = info.toDetails(id);
self.emit('address', receive, change, map); self.emit('address', id, receive, change, details);
self.fire(id, 'address', receive, change, details);
next(); next();
}); });
}, callback); }, callback);
@ -314,17 +303,17 @@ WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
* Derive new addresses and emit balance. * Derive new addresses and emit balance.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {WalletMap} map * @param {WalletMap} info
* @param {Function} callback * @param {Function} callback
*/ */
WalletDB.prototype.handleTX = function handleTX(tx, map, callback) { WalletDB.prototype.handleTX = function handleTX(tx, info, callback) {
var self = this; var self = this;
this.syncOutputs(tx, map, function(err) { this.syncOutputs(tx, info, function(err) {
if (err) if (err)
return callback(err); return callback(err);
self.updateBalances(tx, map, callback); self.updateBalances(tx, info, callback);
}); });
}; };
@ -1366,9 +1355,9 @@ WalletDB.prototype.fetchWallet = function fetchWallet(id, callback, handler) {
}); });
}; };
WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, map, callback) { WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, info, callback) {
this.fetchWallet(id, callback, function(wallet, callback) { this.fetchWallet(id, callback, function(wallet, callback) {
wallet.syncOutputDepth(map, callback); wallet.syncOutputDepth(info, callback);
}); });
}; };
@ -1591,6 +1580,7 @@ Path.prototype.toJSON = function toJSON() {
return { return {
id: this.id, id: this.id,
name: this.name, name: this.name,
change: this.change === 1,
path: this.toPath() path: this.toPath()
}; };
}; };
@ -1636,48 +1626,6 @@ Path.prototype.toKey = function toKey() {
return this.id + '/' + this.account; return this.id + '/' + this.account;
}; };
/**
* Convert path to a compact json object.
* @returns {Object}
*/
Path.prototype.toCompact = function toCompact() {
return {
path: 'm/' + this.change + '/' + this.index,
address: this.address ? this.address.toBase58() : null
};
};
/**
* Inject properties from compact json object.
* @private
* @param {Object} json
*/
Path.prototype.fromCompact = function fromCompact(json) {
var indexes = bcoin.hd.parsePath(json.path, constants.hd.MAX_INDEX);
assert(indexes.length === 2);
this.change = indexes[0];
this.index = indexes[1];
this.address = json.address
? bcoin.address.fromBase58(json.address)
: null;
return this;
};
/**
* Instantiate path from compact json object.
* @param {Object} json
* @returns {Path}
*/
Path.fromCompact = function fromCompact(json) {
return new Path().fromCompact(json);
};
/** /**
* Inspect the path. * Inspect the path.
* @returns {String} * @returns {String}