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) {
|
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 Input = require('../primitives/input');
|
||||||
var Output = require('../primitives/output');
|
var Output = require('../primitives/output');
|
||||||
var LRU = require('../utils/lru');
|
var LRU = require('../utils/lru');
|
||||||
|
var PathInfo = require('./pathinfo');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BIP44 Wallet
|
* BIP44 Wallet
|
||||||
@ -1823,6 +1824,37 @@ Wallet.prototype.addTX = function addTX(tx) {
|
|||||||
return this.db.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).
|
* Get all transactions in transaction history (accesses db).
|
||||||
* @param {(String|Number)?} acct
|
* @param {(String|Number)?} acct
|
||||||
|
|||||||
@ -25,6 +25,7 @@ var ldb = require('../db/ldb');
|
|||||||
var Bloom = require('../utils/bloom');
|
var Bloom = require('../utils/bloom');
|
||||||
var Logger = require('../node/logger');
|
var Logger = require('../node/logger');
|
||||||
var TX = require('../primitives/tx');
|
var TX = require('../primitives/tx');
|
||||||
|
var PathInfo = require('./pathinfo');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Database Layout:
|
* Database Layout:
|
||||||
@ -1163,38 +1164,14 @@ WalletDB.prototype.resend = co(function* resend() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a transactions' addresses to wallet IDs.
|
* Get all wallet ids by multiple address hashes.
|
||||||
* @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.
|
|
||||||
* @param {Hash[]} hashes
|
* @param {Hash[]} hashes
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype.getWalletsByHashes = co(function* getWalletsByHashes(hashes) {
|
WalletDB.prototype.getWidsByHashes = co(function* getWidsByHashes(hashes) {
|
||||||
var map = {};
|
|
||||||
var result = [];
|
var result = [];
|
||||||
var i, j, hash, wids, wid, wallet, item, path;
|
var i, j, hash, wids, wid;
|
||||||
|
|
||||||
for (i = 0; i < hashes.length; i++) {
|
for (i = 0; i < hashes.length; i++) {
|
||||||
hash = hashes[i];
|
hash = hashes[i];
|
||||||
@ -1204,61 +1181,13 @@ WalletDB.prototype.getWalletsByHashes = co(function* getWalletsByHashes(hashes)
|
|||||||
|
|
||||||
wids = yield this.getWalletsByHash(hash);
|
wids = yield this.getWalletsByHash(hash);
|
||||||
|
|
||||||
for (j = 0; j < wids.length; j++) {
|
for (j = 0; j < wids.length; j++)
|
||||||
wid = wids[j];
|
utils.binaryInsert(result, wids[j], compare, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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.
|
* Write the genesis block as the best hash.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
@ -1324,7 +1253,7 @@ WalletDB.prototype.writeBlock = function writeBlock(block, matches) {
|
|||||||
for (i = 0; i < block.hashes.length; i++) {
|
for (i = 0; i < block.hashes.length; i++) {
|
||||||
hash = block.hashes[i];
|
hash = block.hashes[i];
|
||||||
wallets = matches[i];
|
wallets = matches[i];
|
||||||
batch.put(layout.e(hash), serializeInfo(wallets));
|
batch.put(layout.e(hash), serializeWallets(wallets));
|
||||||
}
|
}
|
||||||
|
|
||||||
return batch.write();
|
return batch.write();
|
||||||
@ -1422,7 +1351,7 @@ WalletDB.prototype._addBlock = co(function* addBlock(entry, txs) {
|
|||||||
for (i = 0; i < txs.length; i++) {
|
for (i = 0; i < txs.length; i++) {
|
||||||
tx = txs[i];
|
tx = txs[i];
|
||||||
|
|
||||||
wallets = yield this._addTX(tx);
|
wallets = yield this._add(tx);
|
||||||
|
|
||||||
if (!wallets)
|
if (!wallets)
|
||||||
continue;
|
continue;
|
||||||
@ -1488,20 +1417,7 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
|
|||||||
|
|
||||||
for (i = 0; i < block.hashes.length; i++) {
|
for (i = 0; i < block.hashes.length; i++) {
|
||||||
hash = block.hashes[i];
|
hash = block.hashes[i];
|
||||||
wallets = yield this.getWalletsByTX(hash);
|
yield this._unconfirm(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tip = block.hash;
|
this.tip = block.hash;
|
||||||
@ -1516,10 +1432,11 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
|
|||||||
* @returns {Promise}
|
* @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();
|
var unlock = yield this.txLock.lock();
|
||||||
try {
|
try {
|
||||||
return yield this._addTX(tx);
|
return yield this._add(tx);
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
@ -1532,17 +1449,18 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype._addTX = co(function* addTX(tx, force) {
|
WalletDB.prototype._add = co(function* add(tx) {
|
||||||
var i, wallets, info, wallet;
|
var i, hashes, wallets, wid, wallet;
|
||||||
|
|
||||||
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
||||||
|
|
||||||
// Atomicity doesn't matter here. If we crash,
|
// Atomicity doesn't matter here. If we crash,
|
||||||
// the automatic rescan will get the database
|
// the automatic rescan will get the database
|
||||||
// back in the correct state.
|
// 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;
|
return;
|
||||||
|
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
@ -1550,313 +1468,61 @@ WalletDB.prototype._addTX = co(function* addTX(tx, force) {
|
|||||||
wallets.length, tx.rhash);
|
wallets.length, tx.rhash);
|
||||||
|
|
||||||
for (i = 0; i < wallets.length; i++) {
|
for (i = 0; i < wallets.length; i++) {
|
||||||
info = wallets[i];
|
wid = wallets[i];
|
||||||
wallet = info.wallet;
|
wallet = yield this.get(wid);
|
||||||
|
|
||||||
if (!wallet)
|
if (!wallet)
|
||||||
continue;
|
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.add(tx);
|
||||||
yield wallet.handleTX(info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallets;
|
return wallets;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path Info
|
* Add a transaction to the database, map addresses
|
||||||
* @constructor
|
* to wallet IDs, potentially store orphans, resolve
|
||||||
* @param {WalletDB} db
|
* orphans, or confirm a transaction.
|
||||||
* @param {WalletID} wid
|
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Object} table
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function PathInfo(wallet, tx, paths) {
|
WalletDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||||
if (!(this instanceof PathInfo))
|
var unlock = yield this.txLock.lock();
|
||||||
return new PathInfo(wallet, tx, paths);
|
try {
|
||||||
|
return yield this._unconfirm(tx);
|
||||||
// All relevant Accounts for
|
} finally {
|
||||||
// inputs and outputs (for database indexing).
|
unlock();
|
||||||
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.
|
* Add a transaction to the database without a lock.
|
||||||
* @private
|
* @private
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Object} table
|
* @returns {Promise}
|
||||||
* @returns {PathInfo}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PathInfo.prototype.fromTX = function fromTX(tx, paths) {
|
WalletDB.prototype._unconfirm = co(function* unconfirm(hash) {
|
||||||
var uniq = {};
|
var wallets = yield this.getWalletsByTX(hash);
|
||||||
var i, j, hashes, hash, paths, path;
|
var i, wid, wallet;
|
||||||
|
|
||||||
this.tx = tx;
|
if (!wallets)
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
return this.pathMap[hash];
|
for (i = 0; i < wallets.length; i++) {
|
||||||
};
|
wid = wallets[i];
|
||||||
|
wallet = yield this.get(wid);
|
||||||
|
|
||||||
/**
|
if (!wallet)
|
||||||
* Convert path info to transaction details.
|
continue;
|
||||||
* @returns {Details}
|
|
||||||
*/
|
|
||||||
|
|
||||||
PathInfo.prototype.toDetails = function toDetails() {
|
yield wallet.unconfirm(hash);
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Wallet Block
|
||||||
@ -2038,21 +1704,8 @@ function serializeWallets(wallets) {
|
|||||||
return p.render();
|
return p.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeInfo(wallets) {
|
function compare(a, b) {
|
||||||
var p = new BufferWriter();
|
return a - b;
|
||||||
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 = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user