wallet: path handling.
This commit is contained in:
parent
9a3e3fba3a
commit
aa8f9fdf90
296
lib/wallet/pathinfo.js
Normal file
296
lib/wallet/pathinfo.js
Normal file
@ -0,0 +1,296 @@
|
||||
var utils = require('../utils/utils');
|
||||
|
||||
/**
|
||||
* Path Info
|
||||
* @constructor
|
||||
* @param {WalletDB} db
|
||||
* @param {WalletID} wid
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
*/
|
||||
|
||||
function PathInfo(wallet, tx, paths) {
|
||||
if (!(this instanceof PathInfo))
|
||||
return new PathInfo(wallet, tx, paths);
|
||||
|
||||
// All relevant Accounts for
|
||||
// inputs and outputs (for database indexing).
|
||||
this.accounts = [];
|
||||
|
||||
// All output paths (for deriving during sync).
|
||||
this.paths = [];
|
||||
|
||||
// Wallet
|
||||
this.wallet = wallet;
|
||||
|
||||
// Wallet ID
|
||||
this.wid = wallet.wid;
|
||||
|
||||
// Wallet Label
|
||||
this.id = wallet.id;
|
||||
|
||||
// Map of address hashes->paths.
|
||||
this.pathMap = {};
|
||||
|
||||
// Current transaction.
|
||||
this.tx = null;
|
||||
|
||||
// Wallet-specific details cache.
|
||||
this._details = null;
|
||||
this._json = null;
|
||||
|
||||
if (tx)
|
||||
this.fromTX(tx, paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate path info from a transaction.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
* @returns {PathInfo}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.fromTX = function fromTX(tx, paths) {
|
||||
var uniq = {};
|
||||
var i, j, hashes, hash, paths, path;
|
||||
|
||||
this.tx = tx;
|
||||
|
||||
for (i = 0; i < paths.length; i++) {
|
||||
path = paths[i];
|
||||
|
||||
this.pathMap[path.hash] = path;
|
||||
|
||||
if (!uniq[path.account]) {
|
||||
uniq[path.account] = true;
|
||||
this.accounts.push(path.account);
|
||||
}
|
||||
}
|
||||
|
||||
hashes = tx.getOutputHashes('hex');
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
paths = this.pathMap[hash];
|
||||
this.paths.push(path);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path info from a transaction.
|
||||
* @param {WalletDB} db
|
||||
* @param {WalletID} wid
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
* @returns {PathInfo}
|
||||
*/
|
||||
|
||||
PathInfo.fromTX = function fromTX(wallet, tx, paths) {
|
||||
return new PathInfo(wallet).fromTX(tx, paths);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the map has paths
|
||||
* for a given address hash.
|
||||
* @param {Hash} hash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.hasPath = function hasPath(hash) {
|
||||
if (!hash)
|
||||
return false;
|
||||
|
||||
return this.pathMap[hash] != null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get path for a given address hash.
|
||||
* @param {Hash} hash
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.getPath = function getPath(hash) {
|
||||
if (!hash)
|
||||
return;
|
||||
|
||||
return this.pathMap[hash];
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path info to transaction details.
|
||||
* @returns {Details}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.toDetails = function toDetails() {
|
||||
var details = this._details;
|
||||
|
||||
if (!details) {
|
||||
details = new Details(this);
|
||||
this._details = details;
|
||||
}
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path info to JSON details (caches json).
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.toJSON = function toJSON() {
|
||||
var json = this._json;
|
||||
|
||||
if (!json) {
|
||||
json = this.toDetails().toJSON();
|
||||
this._json = json;
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
module.exports = PathInfo;
|
||||
|
||||
/**
|
||||
* Transaction Details
|
||||
* @constructor
|
||||
* @param {PathInfo} info
|
||||
*/
|
||||
|
||||
function Details(info) {
|
||||
if (!(this instanceof Details))
|
||||
return new Details(info);
|
||||
|
||||
this.db = info.wallet.db;
|
||||
this.network = this.db.network;
|
||||
this.wid = info.wid;
|
||||
this.id = info.id;
|
||||
this.hash = info.tx.hash('hex');
|
||||
this.height = info.tx.height;
|
||||
this.block = info.tx.block;
|
||||
this.index = info.tx.index;
|
||||
this.confirmations = info.tx.getConfirmations(this.db.height);
|
||||
this.fee = info.tx.getFee();
|
||||
this.ts = info.tx.ts;
|
||||
this.ps = info.tx.ps;
|
||||
this.tx = info.tx;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
|
||||
this.init(info.pathMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize transactions details
|
||||
* by pushing on mapped members.
|
||||
* @private
|
||||
* @param {Object} table
|
||||
*/
|
||||
|
||||
Details.prototype.init = function init(map) {
|
||||
this._insert(this.tx.inputs, true, this.inputs, map);
|
||||
this._insert(this.tx.outputs, false, this.outputs, map);
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert members in the input or output vector.
|
||||
* @private
|
||||
* @param {Input[]|Output[]} vector
|
||||
* @param {Array} target
|
||||
* @param {Object} table
|
||||
*/
|
||||
|
||||
Details.prototype._insert = function _insert(vector, input, target, map) {
|
||||
var i, j, io, address, hash, paths, path, member;
|
||||
|
||||
for (i = 0; i < vector.length; i++) {
|
||||
io = vector[i];
|
||||
member = new DetailsMember();
|
||||
|
||||
if (input) {
|
||||
if (io.coin)
|
||||
member.value = io.coin.value;
|
||||
} else {
|
||||
member.value = io.value;
|
||||
}
|
||||
|
||||
address = io.getAddress();
|
||||
|
||||
if (address) {
|
||||
member.address = address;
|
||||
|
||||
hash = address.getHash('hex');
|
||||
path = map[hash];
|
||||
|
||||
if (path)
|
||||
member.path = path;
|
||||
}
|
||||
|
||||
target.push(member);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert details to a more json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Details.prototype.toJSON = function toJSON() {
|
||||
var self = this;
|
||||
return {
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
hash: utils.revHex(this.hash),
|
||||
height: this.height,
|
||||
block: this.block ? utils.revHex(this.block) : null,
|
||||
ts: this.ts,
|
||||
ps: this.ps,
|
||||
index: this.index,
|
||||
fee: utils.btc(this.fee),
|
||||
confirmations: this.confirmations,
|
||||
inputs: this.inputs.map(function(input) {
|
||||
return input.toJSON(self.network);
|
||||
}),
|
||||
outputs: this.outputs.map(function(output) {
|
||||
return output.toJSON(self.network);
|
||||
}),
|
||||
tx: this.tx.toRaw().toString('hex')
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction Details Member
|
||||
* @constructor
|
||||
* @property {Number} value
|
||||
* @property {Address} address
|
||||
* @property {Path} path
|
||||
*/
|
||||
|
||||
function DetailsMember() {
|
||||
if (!(this instanceof DetailsMember))
|
||||
return new DetailsMember();
|
||||
|
||||
this.value = 0;
|
||||
this.address = null;
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the member to a more json-friendly object.
|
||||
* @param {Network} network
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
DetailsMember.prototype.toJSON = function toJSON(network) {
|
||||
return {
|
||||
value: utils.btc(this.value),
|
||||
address: this.address
|
||||
? this.address.toBase58(network)
|
||||
: null,
|
||||
path: this.path
|
||||
? this.path.toJSON()
|
||||
: null
|
||||
};
|
||||
};
|
||||
@ -403,7 +403,7 @@ TXDB.prototype.commit = co(function* commit() {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getInfo = function getInfo(tx) {
|
||||
return this.walletdb.getPathInfo(this.wallet, tx);
|
||||
return this.wallet.getPathInfo(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -28,6 +28,7 @@ var MasterKey = require('./masterkey');
|
||||
var Input = require('../primitives/input');
|
||||
var Output = require('../primitives/output');
|
||||
var LRU = require('../utils/lru');
|
||||
var PathInfo = require('./pathinfo');
|
||||
|
||||
/**
|
||||
* BIP44 Wallet
|
||||
@ -1823,6 +1824,37 @@ Wallet.prototype.addTX = function addTX(tx) {
|
||||
return this.db.addTX(tx);
|
||||
};
|
||||
|
||||
Wallet.prototype.add = co(function* add(tx) {
|
||||
var info = yield this.getPathInfo(tx);
|
||||
yield this.txdb.add(tx, info);
|
||||
yield this.handleTX(info);
|
||||
});
|
||||
|
||||
Wallet.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
return yield this.txdb.unconfirm(hash);
|
||||
});
|
||||
|
||||
/**
|
||||
* Map a transactions' addresses to wallet IDs.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link PathInfo}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.getPathInfo = co(function* getPathInfo(tx) {
|
||||
var hashes = tx.getHashes('hex');
|
||||
var paths = [];
|
||||
var i, hash, path;
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
path = yield this.getPath(hash);
|
||||
if (path)
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
return new PathInfo(this, tx, paths);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all transactions in transaction history (accesses db).
|
||||
* @param {(String|Number)?} acct
|
||||
|
||||
@ -25,6 +25,7 @@ var ldb = require('../db/ldb');
|
||||
var Bloom = require('../utils/bloom');
|
||||
var Logger = require('../node/logger');
|
||||
var TX = require('../primitives/tx');
|
||||
var PathInfo = require('./pathinfo');
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
@ -1163,38 +1164,14 @@ WalletDB.prototype.resend = co(function* resend() {
|
||||
});
|
||||
|
||||
/**
|
||||
* Map a transactions' addresses to wallet IDs.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link PathInfo[}].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.mapWallets = co(function* mapWallets(tx) {
|
||||
var hashes = tx.getHashes('hex');
|
||||
var wallets = yield this.getWalletsByHashes(hashes);
|
||||
var info = [];
|
||||
var i, wallets, item;
|
||||
|
||||
if (wallets.length === 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < wallets.length; i++) {
|
||||
item = wallets[i];
|
||||
info.push(new PathInfo(item.wallet, tx, item.matches));
|
||||
}
|
||||
|
||||
return info;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all wallets by multiple address hashes.
|
||||
* Get all wallet ids by multiple address hashes.
|
||||
* @param {Hash[]} hashes
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getWalletsByHashes = co(function* getWalletsByHashes(hashes) {
|
||||
var map = {};
|
||||
WalletDB.prototype.getWidsByHashes = co(function* getWidsByHashes(hashes) {
|
||||
var result = [];
|
||||
var i, j, hash, wids, wid, wallet, item, path;
|
||||
var i, j, hash, wids, wid;
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
@ -1204,61 +1181,13 @@ WalletDB.prototype.getWalletsByHashes = co(function* getWalletsByHashes(hashes)
|
||||
|
||||
wids = yield this.getWalletsByHash(hash);
|
||||
|
||||
for (j = 0; j < wids.length; j++) {
|
||||
wid = wids[j];
|
||||
item = map[wid];
|
||||
|
||||
if (item) {
|
||||
wallet = item.wallet;
|
||||
path = yield wallet.getPath(hash);
|
||||
assert(path);
|
||||
item.matches.push(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
wallet = yield this.get(wid);
|
||||
assert(wallet);
|
||||
|
||||
path = yield wallet.getPath(hash);
|
||||
assert(path);
|
||||
|
||||
item = new WalletMatch(wallet);
|
||||
item.matches.push(path);
|
||||
|
||||
map[wid] = item;
|
||||
result.push(item);
|
||||
}
|
||||
for (j = 0; j < wids.length; j++)
|
||||
utils.binaryInsert(result, wids[j], compare, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* Map a transactions' addresses to wallet IDs.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link PathInfo}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getPathInfo = co(function* getPathInfo(wallet, tx) {
|
||||
var hashes = tx.getHashes('hex');
|
||||
var paths = [];
|
||||
var i, hash, path;
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
|
||||
if (!this.testFilter(hash))
|
||||
continue;
|
||||
|
||||
path = yield wallet.getPath(hash);
|
||||
|
||||
if (path)
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
return new PathInfo(wallet, tx, paths);
|
||||
});
|
||||
|
||||
/**
|
||||
* Write the genesis block as the best hash.
|
||||
* @returns {Promise}
|
||||
@ -1324,7 +1253,7 @@ WalletDB.prototype.writeBlock = function writeBlock(block, matches) {
|
||||
for (i = 0; i < block.hashes.length; i++) {
|
||||
hash = block.hashes[i];
|
||||
wallets = matches[i];
|
||||
batch.put(layout.e(hash), serializeInfo(wallets));
|
||||
batch.put(layout.e(hash), serializeWallets(wallets));
|
||||
}
|
||||
|
||||
return batch.write();
|
||||
@ -1422,7 +1351,7 @@ WalletDB.prototype._addBlock = co(function* addBlock(entry, txs) {
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
|
||||
wallets = yield this._addTX(tx);
|
||||
wallets = yield this._add(tx);
|
||||
|
||||
if (!wallets)
|
||||
continue;
|
||||
@ -1488,20 +1417,7 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
|
||||
|
||||
for (i = 0; i < block.hashes.length; i++) {
|
||||
hash = block.hashes[i];
|
||||
wallets = yield this.getWalletsByTX(hash);
|
||||
|
||||
if (!wallets)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < wallets.length; j++) {
|
||||
wid = wallets[j];
|
||||
wallet = yield this.get(wid);
|
||||
|
||||
if (!wallet)
|
||||
continue;
|
||||
|
||||
yield wallet.txdb.unconfirm(hash);
|
||||
}
|
||||
yield this._unconfirm(hash);
|
||||
}
|
||||
|
||||
this.tip = block.hash;
|
||||
@ -1516,10 +1432,11 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.addTX = co(function* addTX(tx, force) {
|
||||
WalletDB.prototype.addTX =
|
||||
WalletDB.prototype.add = co(function* add(tx) {
|
||||
var unlock = yield this.txLock.lock();
|
||||
try {
|
||||
return yield this._addTX(tx);
|
||||
return yield this._add(tx);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -1532,17 +1449,18 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype._addTX = co(function* addTX(tx, force) {
|
||||
var i, wallets, info, wallet;
|
||||
WalletDB.prototype._add = co(function* add(tx) {
|
||||
var i, hashes, wallets, wid, wallet;
|
||||
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
||||
|
||||
// Atomicity doesn't matter here. If we crash,
|
||||
// the automatic rescan will get the database
|
||||
// back in the correct state.
|
||||
wallets = yield this.mapWallets(tx);
|
||||
hashes = tx.getHashes('hex');
|
||||
wallets = yield this.getWidsByHashes(hashes);
|
||||
|
||||
if (!wallets)
|
||||
if (wallets.length === 0)
|
||||
return;
|
||||
|
||||
this.logger.info(
|
||||
@ -1550,313 +1468,61 @@ WalletDB.prototype._addTX = co(function* addTX(tx, force) {
|
||||
wallets.length, tx.rhash);
|
||||
|
||||
for (i = 0; i < wallets.length; i++) {
|
||||
info = wallets[i];
|
||||
wallet = info.wallet;
|
||||
wid = wallets[i];
|
||||
wallet = yield this.get(wid);
|
||||
|
||||
if (!wallet)
|
||||
continue;
|
||||
|
||||
this.logger.debug('Adding tx to wallet: %s', info.id);
|
||||
this.logger.debug('Adding tx to wallet: %s', wallet.id);
|
||||
|
||||
yield wallet.txdb.add(tx, info);
|
||||
yield wallet.handleTX(info);
|
||||
yield wallet.add(tx);
|
||||
}
|
||||
|
||||
return wallets;
|
||||
});
|
||||
|
||||
/**
|
||||
* Path Info
|
||||
* @constructor
|
||||
* @param {WalletDB} db
|
||||
* @param {WalletID} wid
|
||||
* Add a transaction to the database, map addresses
|
||||
* to wallet IDs, potentially store orphans, resolve
|
||||
* orphans, or confirm a transaction.
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
function PathInfo(wallet, tx, paths) {
|
||||
if (!(this instanceof PathInfo))
|
||||
return new PathInfo(wallet, tx, paths);
|
||||
|
||||
// All relevant Accounts for
|
||||
// inputs and outputs (for database indexing).
|
||||
this.accounts = [];
|
||||
|
||||
// All output paths (for deriving during sync).
|
||||
this.paths = [];
|
||||
|
||||
// Wallet
|
||||
this.wallet = wallet;
|
||||
|
||||
// Wallet ID
|
||||
this.wid = wallet.wid;
|
||||
|
||||
// Wallet Label
|
||||
this.id = wallet.id;
|
||||
|
||||
// Map of address hashes->paths.
|
||||
this.pathMap = {};
|
||||
|
||||
// Current transaction.
|
||||
this.tx = null;
|
||||
|
||||
// Wallet-specific details cache.
|
||||
this._details = null;
|
||||
this._json = null;
|
||||
|
||||
if (tx)
|
||||
this.fromTX(tx, paths);
|
||||
}
|
||||
WalletDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
var unlock = yield this.txLock.lock();
|
||||
try {
|
||||
return yield this._unconfirm(tx);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Instantiate path info from a transaction.
|
||||
* Add a transaction to the database without a lock.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
* @returns {PathInfo}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.fromTX = function fromTX(tx, paths) {
|
||||
var uniq = {};
|
||||
var i, j, hashes, hash, paths, path;
|
||||
WalletDB.prototype._unconfirm = co(function* unconfirm(hash) {
|
||||
var wallets = yield this.getWalletsByTX(hash);
|
||||
var i, wid, wallet;
|
||||
|
||||
this.tx = tx;
|
||||
|
||||
for (i = 0; i < paths.length; i++) {
|
||||
path = paths[i];
|
||||
|
||||
this.pathMap[path.hash] = path;
|
||||
|
||||
if (!uniq[path.account]) {
|
||||
uniq[path.account] = true;
|
||||
this.accounts.push(path.account);
|
||||
}
|
||||
}
|
||||
|
||||
hashes = tx.getOutputHashes('hex');
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
paths = this.pathMap[hash];
|
||||
this.paths.push(path);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path info from a transaction.
|
||||
* @param {WalletDB} db
|
||||
* @param {WalletID} wid
|
||||
* @param {TX} tx
|
||||
* @param {Object} table
|
||||
* @returns {PathInfo}
|
||||
*/
|
||||
|
||||
PathInfo.fromTX = function fromTX(wallet, tx, paths) {
|
||||
return new PathInfo(wallet).fromTX(tx, paths);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the map has paths
|
||||
* for a given address hash.
|
||||
* @param {Hash} hash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.hasPath = function hasPath(hash) {
|
||||
if (!hash)
|
||||
return false;
|
||||
|
||||
return this.pathMap[hash] != null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get path for a given address hash.
|
||||
* @param {Hash} hash
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.getPath = function getPath(hash) {
|
||||
if (!hash)
|
||||
if (!wallets)
|
||||
return;
|
||||
|
||||
return this.pathMap[hash];
|
||||
};
|
||||
for (i = 0; i < wallets.length; i++) {
|
||||
wid = wallets[i];
|
||||
wallet = yield this.get(wid);
|
||||
|
||||
/**
|
||||
* Convert path info to transaction details.
|
||||
* @returns {Details}
|
||||
*/
|
||||
if (!wallet)
|
||||
continue;
|
||||
|
||||
PathInfo.prototype.toDetails = function toDetails() {
|
||||
var details = this._details;
|
||||
|
||||
if (!details) {
|
||||
details = new Details(this);
|
||||
this._details = details;
|
||||
yield wallet.unconfirm(hash);
|
||||
}
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path info to JSON details (caches json).
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
PathInfo.prototype.toJSON = function toJSON() {
|
||||
var json = this._json;
|
||||
|
||||
if (!json) {
|
||||
json = this.toDetails().toJSON();
|
||||
this._json = json;
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction Details
|
||||
* @constructor
|
||||
* @param {PathInfo} info
|
||||
*/
|
||||
|
||||
function Details(info) {
|
||||
if (!(this instanceof Details))
|
||||
return new Details(info);
|
||||
|
||||
this.db = info.wallet.db;
|
||||
this.network = this.db.network;
|
||||
this.wid = info.wid;
|
||||
this.id = info.id;
|
||||
this.hash = info.tx.hash('hex');
|
||||
this.height = info.tx.height;
|
||||
this.block = info.tx.block;
|
||||
this.index = info.tx.index;
|
||||
this.confirmations = info.tx.getConfirmations(this.db.height);
|
||||
this.fee = info.tx.getFee();
|
||||
this.ts = info.tx.ts;
|
||||
this.ps = info.tx.ps;
|
||||
this.tx = info.tx;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
|
||||
this.init(info.pathMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize transactions details
|
||||
* by pushing on mapped members.
|
||||
* @private
|
||||
* @param {Object} table
|
||||
*/
|
||||
|
||||
Details.prototype.init = function init(map) {
|
||||
this._insert(this.tx.inputs, true, this.inputs, map);
|
||||
this._insert(this.tx.outputs, false, this.outputs, map);
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert members in the input or output vector.
|
||||
* @private
|
||||
* @param {Input[]|Output[]} vector
|
||||
* @param {Array} target
|
||||
* @param {Object} table
|
||||
*/
|
||||
|
||||
Details.prototype._insert = function _insert(vector, input, target, map) {
|
||||
var i, j, io, address, hash, paths, path, member;
|
||||
|
||||
for (i = 0; i < vector.length; i++) {
|
||||
io = vector[i];
|
||||
member = new DetailsMember();
|
||||
|
||||
if (input) {
|
||||
if (io.coin)
|
||||
member.value = io.coin.value;
|
||||
} else {
|
||||
member.value = io.value;
|
||||
}
|
||||
|
||||
address = io.getAddress();
|
||||
|
||||
if (address) {
|
||||
member.address = address;
|
||||
|
||||
hash = address.getHash('hex');
|
||||
path = map[hash];
|
||||
|
||||
if (path)
|
||||
member.path = path;
|
||||
}
|
||||
|
||||
target.push(member);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert details to a more json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Details.prototype.toJSON = function toJSON() {
|
||||
var self = this;
|
||||
return {
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
hash: utils.revHex(this.hash),
|
||||
height: this.height,
|
||||
block: this.block ? utils.revHex(this.block) : null,
|
||||
ts: this.ts,
|
||||
ps: this.ps,
|
||||
index: this.index,
|
||||
fee: utils.btc(this.fee),
|
||||
confirmations: this.confirmations,
|
||||
inputs: this.inputs.map(function(input) {
|
||||
return input.toJSON(self.network);
|
||||
}),
|
||||
outputs: this.outputs.map(function(output) {
|
||||
return output.toJSON(self.network);
|
||||
}),
|
||||
tx: this.tx.toRaw().toString('hex')
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction Details Member
|
||||
* @constructor
|
||||
* @property {Number} value
|
||||
* @property {Address} address
|
||||
* @property {Path} path
|
||||
*/
|
||||
|
||||
function DetailsMember() {
|
||||
if (!(this instanceof DetailsMember))
|
||||
return new DetailsMember();
|
||||
|
||||
this.value = 0;
|
||||
this.address = null;
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the member to a more json-friendly object.
|
||||
* @param {Network} network
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
DetailsMember.prototype.toJSON = function toJSON(network) {
|
||||
return {
|
||||
value: utils.btc(this.value),
|
||||
address: this.address
|
||||
? this.address.toBase58(network)
|
||||
: null,
|
||||
path: this.path
|
||||
? this.path.toJSON()
|
||||
: null
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Wallet Block
|
||||
@ -2038,21 +1704,8 @@ function serializeWallets(wallets) {
|
||||
return p.render();
|
||||
}
|
||||
|
||||
function serializeInfo(wallets) {
|
||||
var p = new BufferWriter();
|
||||
var i, info;
|
||||
|
||||
for (i = 0; i < wallets.length; i++) {
|
||||
info = wallets[i];
|
||||
p.writeU32(info.wid);
|
||||
}
|
||||
|
||||
return p.render();
|
||||
}
|
||||
|
||||
function WalletMatch(wallet) {
|
||||
this.wallet = wallet;
|
||||
this.matches = [];
|
||||
function compare(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user