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

View File

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

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.
* This is used for deriving new addresses when
* a confirmed transaction is seen.
* @param {WalletMap} map
* @param {WalletMap} info
* @param {Function} callback - Returns [Errr, Boolean]
* (true if new addresses were allocated).
*/
Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) {
Wallet.prototype.syncOutputDepth = function syncOutputDepth(info, callback) {
var self = this;
var change = [];
var receive = [];
var accounts = {};
var i, path, unlock;
unlock = this.writeLock.lock(syncOutputDepth, [map, callback]);
unlock = this.writeLock.lock(syncOutputDepth, [info, callback]);
if (!unlock)
return;
@ -1050,8 +1051,22 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) {
this.start();
utils.forEachSerial(map.outputs, function(output, next) {
var paths = output.paths;
for (i = 0; i < info.paths.length; i++) {
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 changeDepth = -1;
@ -1070,7 +1085,7 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(map, callback) {
receiveDepth += 2;
changeDepth += 2;
self.getAccount(output.account, function(err, account) {
self.getAccount(account, function(err, account) {
if (err)
return next(err);

View File

@ -101,35 +101,35 @@ WalletDB.prototype._init = function _init() {
});
}
function handleEvent(event, tx, map) {
var i, path;
function handleEvent(event, tx, info) {
var i, id, details;
self.emit(event, tx, map);
for (i = 0; i < map.accounts.length; i++) {
path = map.accounts[i];
self.fire(path.id, event, tx, path.name);
for (i = 0; i < info.wallets.length; i++) {
id = info.wallets[i];
details = info.toDetails(id);
self.emit(event, id, tx, details);
self.fire(id, event, tx, details);
}
}
this.tx.on('tx', function(tx, map) {
handleEvent('tx', tx, map);
this.tx.on('tx', function(tx, info) {
handleEvent('tx', tx, info);
});
this.tx.on('conflict', function(tx, map) {
handleEvent('conflict', tx, map);
this.tx.on('conflict', function(tx, info) {
handleEvent('conflict', tx, info);
});
this.tx.on('confirmed', function(tx, map) {
handleEvent('confirmed', tx, map);
this.tx.on('confirmed', function(tx, info) {
handleEvent('confirmed', tx, info);
});
this.tx.on('unconfirmed', function(tx, map) {
handleEvent('unconfirmed', tx, map);
this.tx.on('unconfirmed', function(tx, info) {
handleEvent('unconfirmed', tx, info);
});
this.tx.on('updated', function(tx, map) {
handleEvent('updated', tx, map);
this.tx.on('updated', function(tx, info) {
handleEvent('updated', tx, info);
});
};
@ -248,16 +248,15 @@ WalletDB.prototype.commit = function commit(id, callback) {
* Emit balance events after a tx is saved.
* @private
* @param {TX} tx
* @param {WalletMap} map
* @param {WalletMap} info
* @param {Function} callback
*/
WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
WalletDB.prototype.updateBalances = function updateBalances(tx, info, callback) {
var self = this;
var balances = {};
var i, id, keys;
var details;
utils.forEachSerial(map.getOutputWallets(), function(id, next) {
utils.forEachSerial(info.wallets, function(id, next) {
if (self.listeners('balances').length === 0
&& !self.hasListener(id, 'balance')) {
return next();
@ -267,44 +266,34 @@ WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
if (err)
return next(err);
balances[id] = balance;
details = info.toDetails(id);
self.emit('balance', id, balance, details);
self.fire(id, 'balance', balance, details);
next();
});
}, function(err) {
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);
});
}, callback);
};
/**
* Derive new addresses after a tx is saved.
* @private
* @param {TX} tx
* @param {WalletMap} map
* @param {WalletMap} info
* @param {Function} callback
*/
WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
WalletDB.prototype.syncOutputs = function syncOutputs(tx, info, callback) {
var self = this;
var details;
utils.forEachSerial(map.getOutputWallets(), function(id, next) {
self.syncOutputDepth(id, map, function(err, receive, change) {
utils.forEachSerial(info.wallets, function(id, next) {
self.syncOutputDepth(id, info, function(err, receive, change) {
if (err)
return next(err);
self.fire(id, 'address', receive, change);
self.emit('address', receive, change, map);
details = info.toDetails(id);
self.emit('address', id, receive, change, details);
self.fire(id, 'address', receive, change, details);
next();
});
}, callback);
@ -314,17 +303,17 @@ WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
* Derive new addresses and emit balance.
* @private
* @param {TX} tx
* @param {WalletMap} map
* @param {WalletMap} info
* @param {Function} callback
*/
WalletDB.prototype.handleTX = function handleTX(tx, map, callback) {
WalletDB.prototype.handleTX = function handleTX(tx, info, callback) {
var self = this;
this.syncOutputs(tx, map, function(err) {
this.syncOutputs(tx, info, function(err) {
if (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) {
wallet.syncOutputDepth(map, callback);
wallet.syncOutputDepth(info, callback);
});
};
@ -1591,6 +1580,7 @@ Path.prototype.toJSON = function toJSON() {
return {
id: this.id,
name: this.name,
change: this.change === 1,
path: this.toPath()
};
};
@ -1636,48 +1626,6 @@ Path.prototype.toKey = function toKey() {
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.
* @returns {String}