diff --git a/lib/wallet/index.js b/lib/wallet/index.js index 06b00b65..483c7cf8 100644 --- a/lib/wallet/index.js +++ b/lib/wallet/index.js @@ -3,7 +3,6 @@ exports.Account = require('./account'); exports.MasterKey = require('./masterkey'); exports.Path = require('./path'); -exports.PathInfo = require('./pathinfo'); exports.TXDB = require('./txdb'); exports.WalletDB = require('./walletdb'); exports.Wallet = require('./wallet'); diff --git a/lib/wallet/pathinfo.js b/lib/wallet/pathinfo.js deleted file mode 100644 index d3e462a3..00000000 --- a/lib/wallet/pathinfo.js +++ /dev/null @@ -1,313 +0,0 @@ -/*! - * pathinfo.js - pathinfo object for bcoin - * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -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, hashes, hash, 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]; - path = this.pathMap[hash]; - if (path) - 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(output) { - var hash = output.getHash('hex'); - - 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(output) { - var hash = output.getHash('hex'); - - 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; -}; - -/** - * 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, io, address, hash, 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 - }; -}; - -/* - * Expose - */ - -module.exports = PathInfo; diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 3f0b3d00..5080647d 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -34,6 +34,7 @@ var DUMMY = new Buffer([0]); * M[account][time][hash] -> dummy (tx by time + account) * H[account][height][hash] -> dummy (tx by height + account) * C[account][hash][index] -> dummy (coin by account) + * r[hash] -> dummy (replace by fee chain) */ var layout = { @@ -324,7 +325,7 @@ TXDB.prototype.commit = co(function* commit() { * @private * @param {String} event * @param {Object} data - * @param {PathInfo} details + * @param {Details} details */ TXDB.prototype.emit = function emit(event, data, details) { @@ -424,9 +425,9 @@ TXDB.prototype.values = function values(options) { }; /** - * Map a transactions' addresses to wallet IDs. - * @param {TX} tx - * @returns {Promise} - Returns {@link PathInfo}. + * Get wallet path for output. + * @param {Output} output + * @returns {Promise} - Returns {@link Path}. */ TXDB.prototype.getPath = function getPath(output) { @@ -436,9 +437,9 @@ TXDB.prototype.getPath = function getPath(output) { }; /** - * Map a transactions' addresses to wallet IDs. - * @param {TX} tx - * @returns {Promise} - Returns {@link PathInfo}. + * Test whether path exists for output. + * @param {Output} output + * @returns {Promise} - Returns Boolean. */ TXDB.prototype.hasPath = function hasPath(output) { @@ -497,12 +498,9 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) { input = tx.inputs[i]; prevout = input.prevout; - spent = yield this.getSpent(prevout.hash, prevout.index); - - if (spent) { - coin = yield this.getSpentCoin(spent, prevout); - assert(coin); + coin = yield this.getCoin(prevout.hash, prevout.index); + if (coin) { if (this.options.verify && tx.height === -1) { input.coin = coin; if (!(yield tx.verifyInputAsync(i))) @@ -512,9 +510,12 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) { continue; } - coin = yield this.getCoin(prevout.hash, prevout.index); + spent = yield this.getSpent(prevout.hash, prevout.index); + + if (spent) { + coin = yield this.getSpentCoin(spent, prevout); + assert(coin); - if (coin) { if (this.options.verify && tx.height === -1) { input.coin = coin; if (!(yield tx.verifyInputAsync(i))) @@ -2373,8 +2374,12 @@ TXDB.prototype.abandon = co(function* abandon(hash) { return yield this.remove(hash); }); -/* +/** * Balance + * @constructor + * @param {WalletID} wid + * @param {String} id + * @param {Number} account */ function Balance(wid, id, account) { @@ -2388,12 +2393,24 @@ function Balance(wid, id, account) { this.confirmed = 0; } +/** + * Test whether a balance is equal. + * @param {Balance} balance + * @returns {Boolean} + */ + Balance.prototype.equal = function equal(balance) { return this.wid === balance.wid && this.confirmed === balance.confirmed && this.unconfirmed === balance.unconfirmed; }; +/** + * Convert balance to a more json-friendly object. + * @param {Boolean?} minimal + * @returns {Object} + */ + Balance.prototype.toJSON = function toJSON(minimal) { return { wid: !minimal ? this.wid : undefined, @@ -2404,6 +2421,11 @@ Balance.prototype.toJSON = function toJSON(minimal) { }; }; +/** + * Convert balance to human-readable string. + * @returns {String} + */ + Balance.prototype.toString = function toString() { return ''; }; +/** + * Inspect balance. + * @param {String} + */ + Balance.prototype.inspect = function inspect() { return this.toString(); }; @@ -2418,6 +2445,8 @@ Balance.prototype.inspect = function inspect() { /** * Chain State * @constructor + * @param {WalletID} wid + * @param {String} id */ function TXDBState(wid, id) { @@ -2430,6 +2459,11 @@ function TXDBState(wid, id) { this.committed = false; } +/** + * Clone the state. + * @returns {TXDBState} + */ + TXDBState.prototype.clone = function clone() { var state = new TXDBState(this.wid, this.id); state.tx = this.tx; @@ -2439,11 +2473,21 @@ TXDBState.prototype.clone = function clone() { return state; }; +/** + * Commit and serialize state. + * @returns {Buffer} + */ + TXDBState.prototype.commit = function commit() { this.committed = true; return this.toRaw(); }; +/** + * Convert state to a balance object. + * @returns {Balance} + */ + TXDBState.prototype.toBalance = function toBalance() { var balance = new Balance(this.wid, this.id, -1); balance.unconfirmed = this.unconfirmed; @@ -2451,6 +2495,11 @@ TXDBState.prototype.toBalance = function toBalance() { return balance; }; +/** + * Serialize state. + * @returns {Buffer} + */ + TXDBState.prototype.toRaw = function toRaw(writer) { var p = new BufferWriter(writer); @@ -2465,6 +2514,13 @@ TXDBState.prototype.toRaw = function toRaw(writer) { return p; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @returns {TXDBState} + */ + TXDBState.prototype.fromRaw = function fromRaw(data) { var p = new BufferReader(data); this.tx = p.readU53(); @@ -2474,10 +2530,22 @@ TXDBState.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Instantiate txdb state from serialized data. + * @param {Buffer} data + * @returns {TXDBState} + */ + TXDBState.fromRaw = function fromRaw(wid, id, data) { return new TXDBState(wid, id).fromRaw(data); }; +/** + * Convert state to a more json-friendly object. + * @param {Boolean?} minimal + * @returns {Object} + */ + TXDBState.prototype.toJSON = function toJSON(minimal) { return { wid: !minimal ? this.wid : undefined, @@ -2489,28 +2557,20 @@ TXDBState.prototype.toJSON = function toJSON(minimal) { }; }; +/** + * Inspect the state. + * @returns {Object} + */ + TXDBState.prototype.inspect = function inspect() { return this.toJSON(); }; -/* - * Helpers - */ - -function Orphan(tx, i) { - this.tx = tx; - this.hash = tx.hash('hex'); - this.index = i; -} - -function cmp(a, b) { - return a - b; -} - /** * Transaction Details * @constructor - * @param {PathInfo} info + * @param {TXDB} txdb + * @param {TX} tx */ function Details(txdb, tx) { @@ -2538,6 +2598,11 @@ function Details(txdb, tx) { this.init(); } +/** + * Initialize transaction details. + * @private + */ + Details.prototype.init = function init() { var i, input, output, member; @@ -2557,6 +2622,13 @@ Details.prototype.init = function init() { } }; +/** + * Add necessary info to input member. + * @param {Number} i + * @param {Path} path + * @param {Coin} coin + */ + Details.prototype.setInput = function setInput(i, path, coin) { var member = this.inputs[i]; @@ -2571,6 +2643,12 @@ Details.prototype.setInput = function setInput(i, path, coin) { } }; +/** + * Add necessary info to output member. + * @param {Number} i + * @param {Path} path + */ + Details.prototype.setOutput = function setOutput(i, path) { var member = this.outputs[i]; @@ -2644,8 +2722,12 @@ DetailsMember.prototype.toJSON = function toJSON(network) { }; /** - * Credit + * Credit (wrapped coin) * @constructor + * @param {Coin} coin + * @param {Boolean?} spent + * @property {Coin} coin + * @property {Boolean} spent */ function Credit(coin, spent) { @@ -2656,6 +2738,12 @@ function Credit(coin, spent) { this.spent = spent || false; } +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + Credit.prototype.fromRaw = function fromRaw(data) { var p = BufferReader(data); this.coin.fromRaw(p); @@ -2663,10 +2751,21 @@ Credit.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Instantiate credit from serialized data. + * @param {Buffer} data + * @returns {Credit} + */ + Credit.fromRaw = function fromRaw(data) { return new Credit().fromRaw(data); }; +/** + * Serialize credit. + * @returns {Buffer} + */ + Credit.prototype.toRaw = function toRaw(writer) { var p = BufferWriter(writer); @@ -2679,16 +2778,45 @@ Credit.prototype.toRaw = function toRaw(writer) { return p; }; +/** + * Inject properties from tx object. + * @private + * @param {TX} tx + * @param {Number} i + * @returns {Credit} + */ + Credit.prototype.fromTX = function fromTX(tx, i) { this.coin.fromTX(tx, i); this.spent = false; return this; }; +/** + * Instantiate credit from transaction. + * @param {TX} tx + * @param {Number} i + * @returns {Credit} + */ + Credit.fromTX = function fromTX(tx, i) { return new Credit().fromTX(tx, i); }; +/* + * Helpers + */ + +function Orphan(tx, i) { + this.tx = tx; + this.hash = tx.hash('hex'); + this.index = i; +} + +function cmp(a, b) { + return a - b; +} + /* * Expose */ diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index c39dca34..6b8856ab 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -1616,9 +1616,8 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) { * Sync address depths based on a transaction's outputs. * This is used for deriving new addresses when * a confirmed transaction is seen. - * @param {PathInfo} info - * @returns {Promise} - Returns Boolean - * (true if new addresses were allocated). + * @param {Details} details + * @returns {Promise} */ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(details) {