diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 6c902ecf..bc9ea644 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -956,7 +956,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { if (this.depth >= 0xff) throw new Error('Depth too high.'); - id = this.xprivkey + '/' + index; + id = this.getID(index); child = HD.cache.get(id); if (child) @@ -1002,6 +1002,19 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { return child; }; +/** + * Unique HD key ID. + * @private + * @param {Number} index + * @returns {String} + */ + +HDPrivateKey.prototype.getID = function getID(index) { + return this.network.keyPrefix.xprivkey58 + + this.publicKey.toString('hex') + + '/' + index; +}; + /** * Derive a BIP44 account key. * @param {Number} accountIndex @@ -1603,7 +1616,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { if (this.depth >= 0xff) throw new Error('Depth too high.'); - id = this.xpubkey + '/' + index; + id = this.getID(index); child = HD.cache.get(id); if (child) @@ -1640,6 +1653,19 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { return child; }; +/** + * Unique HD key ID. + * @private + * @param {Number} index + * @returns {String} + */ + +HDPublicKey.prototype.getID = function getID(index) { + return this.network.keyPrefix.xpubkey58 + + this.publicKey.toString('hex') + + '/' + index; +}; + /** * Derive a BIP44 account key (does not derive, only ensures account key). * @method diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 85478598..e82997e6 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -1493,7 +1493,7 @@ Wallet.prototype.addTX = function addTX(tx, callback) { */ Wallet.prototype.getHistory = function getHistory(account, callback) { - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getHistory(account, callback); }); }; @@ -1505,7 +1505,7 @@ Wallet.prototype.getHistory = function getHistory(account, callback) { */ Wallet.prototype.getCoins = function getCoins(account, callback) { - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getCoins(account, callback); }); }; @@ -1517,7 +1517,7 @@ Wallet.prototype.getCoins = function getCoins(account, callback) { */ Wallet.prototype.getUnconfirmed = function getUnconfirmed(account, callback) { - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getUnconfirmed(account, callback); }); }; @@ -1529,7 +1529,7 @@ Wallet.prototype.getUnconfirmed = function getUnconfirmed(account, callback) { */ Wallet.prototype.getBalance = function getBalance(account, callback) { - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getBalance(account, callback); }); }; @@ -1543,7 +1543,7 @@ Wallet.prototype.getBalance = function getBalance(account, callback) { */ Wallet.prototype.getLastTime = function getLastTime(account, callback) { - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getLastTime(account, callback); }); }; @@ -1561,7 +1561,7 @@ Wallet.prototype.getLast = function getLast(account, limit, callback) { limit = account; account = null; } - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getLast(account, limit, callback); }); }; @@ -1581,7 +1581,7 @@ Wallet.prototype.getTimeRange = function getTimeRange(account, options, callback options = account; account = null; } - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.getTimeRange(account, options, callback); }); }; @@ -1599,7 +1599,7 @@ Wallet.prototype.zap = function zap(account, age, callback) { age = account; account = null; } - this._getKey(account, callback, function(account, callback) { + this._getIndex(account, callback, function(account, callback) { this.tx.zap(account, age, callback); }); }; @@ -1610,15 +1610,8 @@ Wallet.prototype.zap = function zap(account, age, callback) { * @param {Function} callback - Returns [Error]. */ -Wallet.prototype.abandon = function abandon(account, hash, callback) { - if (typeof hash === 'function') { - callback = hash; - hash = account; - account = null; - } - this._getKey(account, callback, function(account, callback) { - this.tx.abandon(account, hash, callback); - }); +Wallet.prototype.abandon = function abandon(hash, callback) { + this.tx.abandon(hash, callback); }; /** @@ -1629,7 +1622,7 @@ Wallet.prototype.abandon = function abandon(account, hash, callback) { * @param {Function} callback */ -Wallet.prototype._getKey = function _getKey(account, errback, callback) { +Wallet.prototype._getIndex = function _getIndex(account, errback, callback) { var self = this; if (typeof account === 'function') { @@ -2715,9 +2708,6 @@ Account.prototype.toRaw = function toRaw(writer) { var i; p.writeU32(this.network.magic); - // NOTE: Passed in by caller. - // p.writeU32(this.wid); - // p.writeVarString(this.id, 'utf8'); p.writeVarString(this.name, 'utf8'); p.writeU8(this.initialized ? 1 : 0); p.writeU8(this.type === 'pubkeyhash' ? 0 : 1); @@ -2751,9 +2741,6 @@ Account.prototype.fromRaw = function fromRaw(data) { var i, count; this.network = bcoin.network.fromMagic(p.readU32()); - // NOTE: Passed in by caller. - // this.wid = p.readU32(); - // this.id = p.readVarString('utf8'); this.name = p.readVarString('utf8'); this.initialized = p.readU8() === 1; this.type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig'; @@ -3039,8 +3026,9 @@ MasterKey.prototype.encrypt = function encrypt(passphrase, callback) { if (!passphrase) return callback(); - iv = bcoin.ec.random(16); data = this.key.toExtended(); + iv = bcoin.ec.random(16); + this.stop(); utils.encrypt(data, passphrase, iv, function(err, data) { diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 6ebb5483..0c081401 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -9,15 +9,15 @@ /* * Database Layout: - * (inherits all from txdb) - * p/[address] -> wid & path data + * p/[address] -> path data * w/[wid] -> wallet - * l/[label] -> wallet wid + * l/[id] -> wid * a/[wid]/[index] -> account * i/[wid]/[name] -> account index + * t/[wid]/* -> txdb * R -> tip * b/[hash] -> wallet block - * t/[hash] -> tx->wallet-wid map + * t/[hash] -> tx->wid map */ var bcoin = require('./env'); @@ -186,7 +186,7 @@ WalletDB.prototype.getDepth = function getDepth(callback) { // This may seem like a strange way to do // this, but updating a global state when // creating a new wallet is actually pretty - // damn tricky. They would be major atomicity + // damn tricky. There would be major atomicity // issues if updating a global state inside // a "scoped" state. So, we avoid all the // nonsense of adding a global lock to @@ -290,8 +290,8 @@ WalletDB.prototype.loadFilter = function loadFilter(callback) { return callback(); this.db.iterate({ - gte: 'W', - lte: 'W~', + gte: 'p/' + constants.NULL_HASH, + lte: 'p/' + constants.HIGH_HASH, transform: function(key) { key = key.split('/')[1]; self.filter.add(key, 'hex'); @@ -563,12 +563,12 @@ WalletDB.prototype.create = function create(options, callback) { /** * Test for the existence of a wallet. - * @param {WalletID?} wid + * @param {WalletID} id * @param {Function} callback */ -WalletDB.prototype.has = function has(wid, callback) { - this.getWalletID(wid, function(err, wid) { +WalletDB.prototype.has = function has(id, callback) { + this.getWalletID(id, function(err, wid) { if (err) return callback(err); return callback(null, wid != null); @@ -577,7 +577,6 @@ WalletDB.prototype.has = function has(wid, callback) { /** * Attempt to create wallet, return wallet if already exists. - * @param {WalletID?} wid * @param {Object} options - See {@link Wallet}. * @param {Function} callback */ @@ -663,9 +662,6 @@ WalletDB.prototype.getAccounts = function getAccounts(wid, callback) { var map = []; var i, accounts; - if (!utils.isNumber(wid)) - return callback(new Error('Wallet IDs must be alphanumeric.')); - this.db.iterate({ gte: 'i/' + pad32(wid) + '/', lte: 'i/' + pad32(wid) + '/~', @@ -944,8 +940,8 @@ WalletDB.prototype.getAddresses = function getAddresses(wid, callback) { } this.db.iterate({ - gte: 'W', - lte: 'W~', + gte: 'p/' + constants.NULL_HASH, + lte: 'p/' + constants.HIGH_HASH, values: true, parse: function(value, key) { var paths = parsePaths(value); @@ -965,8 +961,8 @@ WalletDB.prototype.getAddresses = function getAddresses(wid, callback) { WalletDB.prototype.getWallets = function getWallets(callback) { this.db.iterate({ - gte: 'w', - lte: 'w~', + gte: 'l/', + lte: 'l/~', transform: function(key) { return key.split('/')[1]; } @@ -1194,15 +1190,7 @@ WalletDB.prototype.writeBlock = function writeBlock(block, matches, callback) { } } - batch.write(function(err) { - if (err) - return callback(err); - - self.tip = block.hash; - self.height = block.height; - - return callback(); - }); + batch.write(callback); }; /** @@ -1219,15 +1207,7 @@ WalletDB.prototype.unwriteBlock = function unwriteBlock(block, callback) { batch.put('R', prev.toTip()); batch.del('b/' + block.hash); - batch.write(function(err) { - if (err) - return callback(err); - - self.tip = prev.hash; - self.height = prev.height; - - return callback(); - }); + batch.write(callback); }; /** @@ -1277,6 +1257,11 @@ WalletDB.prototype.addBlock = function addBlock(entry, txs, callback, force) { block = WalletBlock.fromEntry(entry); matches = []; + // Update these early so transactions + // get correct confirmation calculations. + this.tip = block.hash; + this.height = block.height; + // NOTE: Atomicity doesn't matter here. If we crash // during this loop, the automatic rescan will get // the database back into the correct state. @@ -1359,7 +1344,15 @@ WalletDB.prototype.removeBlock = function removeBlock(entry, callback, force) { wallet.tx.unconfirm(hash, next); }); - }, callback); + }, function(err) { + if (err) + return callback(err); + + self.tip = block.hash; + self.height = block.height; + + return callback(); + }); }); }); }); diff --git a/test/chain-test.js b/test/chain-test.js index b5076b37..2d87df73 100644 --- a/test/chain-test.js +++ b/test/chain-test.js @@ -222,8 +222,8 @@ describe('Chain', function() { var txs = []; walletdb.getAddresses(function(err, hashes) { assert.ifError(err); - chain.db.scan(null, hashes, function(tx, block, next) { - txs.push(tx); + chain.db.scan(null, hashes, function(block, tx, next) { + txs = txs.concat(tx); next(); }, function(err) { assert.ifError(err);