From 682596fb05e18a2264a9182fa6a650a41dc14b46 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 13 Jul 2016 14:53:37 -0700 Subject: [PATCH] http tests. caches. --- lib/bcoin/chaindb.js | 16 +---- lib/bcoin/http/base.js | 4 +- lib/bcoin/http/client.js | 5 +- lib/bcoin/lru.js | 15 +++++ lib/bcoin/tx.js | 2 +- lib/bcoin/wallet.js | 7 +- lib/bcoin/walletdb.js | 138 ++++++++++++++++++++++++++++++--------- test/http-test.js | 29 +++++++- test/wallet-test.js | 10 +-- 9 files changed, 167 insertions(+), 59 deletions(-) diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 7f4fa6b3..345916eb 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -209,7 +209,7 @@ function ChainDB(chain, options) { // Key size: 66b (* 2) this.coinWindow = ((165 * 1024 + 2300 * 4) + (2300 * 66 * 2)) * 5; - this.coinCache = new NullCache(this.coinWindow); + this.coinCache = new bcoin.lru.nil(this.coinWindow); this.cacheHash = new bcoin.lru(this.cacheWindow, 1); this.cacheHeight = new bcoin.lru(this.cacheWindow, 1); } @@ -1482,20 +1482,6 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { }); }; -/** - * A null cache. Every method is a NOP. - * @constructor - * @param {Number} size - */ - -function NullCache(size) {} - -NullCache.prototype.set = function set(key, value) {}; -NullCache.prototype.remove = function remove(key) {}; -NullCache.prototype.get = function get(key) {}; -NullCache.prototype.has = function has(key) {}; -NullCache.prototype.reset = function reset() {}; - /* * Helpers */ diff --git a/lib/bcoin/http/base.js b/lib/bcoin/http/base.js index 8eed78c1..336204e8 100644 --- a/lib/bcoin/http/base.js +++ b/lib/bcoin/http/base.js @@ -194,7 +194,9 @@ HTTPBase.prototype._initIO = function _initIO() { if (!IOServer) return; - this.io = new IOServer(); + this.io = new IOServer({ + transports: ['websocket'] + }); this.io.attach(this.server); diff --git a/lib/bcoin/http/client.js b/lib/bcoin/http/client.js index 40beedaa..27830f8e 100644 --- a/lib/bcoin/http/client.js +++ b/lib/bcoin/http/client.js @@ -75,7 +75,10 @@ HTTPClient.prototype._open = function _open(callback) { if (!IOClient) return callback(); - this.socket = new IOClient(this.uri); + this.socket = new IOClient(this.uri, { + transports: ['websocket'], + forceNew: true + }); this.socket.on('error', function(err) { self.emit('error', err); diff --git a/lib/bcoin/lru.js b/lib/bcoin/lru.js index 80028525..f4bcdcac 100644 --- a/lib/bcoin/lru.js +++ b/lib/bcoin/lru.js @@ -353,8 +353,23 @@ function LRUItem(key, value) { this.prev = null; } +/** + * A null cache. Every method is a NOP. + * @constructor + * @param {Number} size + */ + +function NullCache(size) {} + +NullCache.prototype.set = function set(key, value) {}; +NullCache.prototype.remove = function remove(key) {}; +NullCache.prototype.get = function get(key) {}; +NullCache.prototype.has = function has(key) {}; +NullCache.prototype.reset = function reset() {}; + /* * Expose */ +LRU.nil = NullCache; module.exports = LRU; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index bdab0ca9..3a827e89 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1868,7 +1868,7 @@ TX.prototype.inspect = function inspect() { value: utils.btc(this.getOutputValue()), fee: utils.btc(this.getFee()), minFee: utils.btc(this.getMinFee()), - rate: utils.btc(this.getRate()), + rate: this.getRate(), // Rate can sometimes exceed 53 bits in testing confirmations: this.getConfirmations(), priority: this.getPriority(), date: utils.date(this.ts || this.ps), diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index ab6f8fe7..65e7d22c 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -1720,12 +1720,15 @@ Account.prototype.fromOptions = function fromOptions(options) { assert(options, 'Options are required.'); assert(utils.isAlpha(options.id), 'Wallet ID must be alphanumeric.'); - assert(utils.isAlpha(options.name), 'Account name must be alphanumeric.'); assert(bcoin.hd.isHD(options.accountKey), 'Account key is required.'); assert(utils.isNumber(options.accountIndex), 'Account index is required.'); this.id = options.id; - this.name = options.name; + + if (options.name != null) { + assert(utils.isAlpha(options.name), 'Account name must be alphanumeric.'); + this.name = options.name; + } if (options.witness != null) { assert(typeof options.witness === 'boolean'); diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 3514a011..fd5e366a 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -54,6 +54,9 @@ function WalletDB(options) { // We need one read lock for `get` and `create`. // It will hold locks specific to wallet ids. this.readLock = new ReadLock(this); + this.accountCache = new bcoin.lru(100000, 1); + this.walletCache = new bcoin.lru(100000, 1); + this.pathCache = new bcoin.lru(100000, 1); this.db = bcoin.ldb({ location: this.options.location, @@ -425,19 +428,13 @@ WalletDB.prototype.get = function get(id, callback) { return callback(null, watcher.object); } - this.db.get('w/' + id, function(err, data) { + this._get(id, function(err, wallet) { if (err) return callback(err); - if (!data) + if (!wallet) return callback(); - try { - wallet = bcoin.wallet.fromRaw(self, data); - } catch (e) { - return callback(e); - } - try { self.register(wallet); } catch (e) { @@ -453,6 +450,37 @@ WalletDB.prototype.get = function get(id, callback) { }); }; +WalletDB.prototype._get = function get(id, callback) { + var self = this; + var wallet; + + if (!id) + return callback(); + + wallet = this.walletCache.get(id); + + if (wallet) + return callback(null, wallet); + + this.db.get('w/' + id, function(err, data) { + if (err) + return callback(err); + + if (!data) + return callback(); + + try { + wallet = bcoin.wallet.fromRaw(self, data); + } catch (e) { + return callback(e); + } + + self.walletCache.set(id, wallet); + + return callback(null, wallet); + }); +}; + /** * Save a wallet to the database. * @param {Wallet} wallet @@ -463,6 +491,8 @@ WalletDB.prototype.save = function save(wallet, callback) { if (!utils.isAlpha(wallet.id)) return callback(new Error('Wallet IDs must be alphanumeric.')); + this.walletCache.set(wallet.id, wallet); + this.db.put('w/' + wallet.id, wallet.toRaw(), callback); }; @@ -480,19 +510,13 @@ WalletDB.prototype.auth = function auth(id, token, callback) { if (!id) return callback(new Error('Wallet not found.')); - this.db.get('w/' + id, function(err, data) { + this._get(id, function(err, wallet) { if (err) return callback(err); - if (!data) + if (!wallet) return callback(new Error('Wallet not found.')); - try { - wallet = bcoin.wallet.fromRaw(self, data); - } catch (e) { - return callback(e); - } - if (typeof token === 'string') { if (!utils.isHex(token)) return callback(new Error('Authentication error.')); @@ -614,19 +638,13 @@ WalletDB.prototype.getAccount = function getAccount(id, name, callback) { if (index === -1) return callback(); - self.db.get('a/' + id + '/' + index, function(err, data) { + self._getAccount(id, index, function(err, account) { if (err) return callback(err); - if (!data) + if (!account) return callback(); - try { - account = bcoin.account.fromRaw(self, data); - } catch (e) { - return callback(e); - } - account.open(function(err) { if (err) return callback(err); @@ -637,6 +655,33 @@ WalletDB.prototype.getAccount = function getAccount(id, name, callback) { }); }; +WalletDB.prototype._getAccount = function getAccount(id, index, callback) { + var self = this; + var key = id + '/' + index; + var account = this.accountCache.get(key); + + if (account) + return callback(null, account); + + this.db.get('a/' + key, function(err, data) { + if (err) + return callback(err); + + if (!data) + return callback(); + + try { + account = bcoin.account.fromRaw(self, data); + } catch (e) { + return callback(e); + } + + self.accountCache.set(key, account); + + return callback(null, account); + }); +}; + /** * List account names and indexes from the db. * @param {WalletID} id @@ -675,6 +720,9 @@ WalletDB.prototype.getAccounts = function getAccounts(id, callback) { */ WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback) { + if (!id) + return callback(null, -1); + if (name == null) return callback(null, -1); @@ -699,7 +747,7 @@ WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback */ WalletDB.prototype.saveAccount = function saveAccount(account, callback) { - var index, batch; + var index, key, batch; if (!utils.isAlpha(account.name)) return callback(new Error('Account names must be alphanumeric.')); @@ -709,9 +757,13 @@ WalletDB.prototype.saveAccount = function saveAccount(account, callback) { index = new Buffer(4); index.writeUInt32LE(account.accountIndex, 0, true); - batch.put('a/' + account.id + '/' + account.accountIndex, account.toRaw()); + key = account.id + '/' + account.accountIndex; + + batch.put('a/' + key, account.toRaw()); batch.put('i/' + account.id + '/' + account.name, index); + this.accountCache.set(key, account); + batch.write(callback); }; @@ -816,7 +868,7 @@ WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) { self.emit('save address', address, path); - self.db.fetch('W/' + hash, parsePaths, function(err, paths) { + self._getPaths(hash, function(err, paths) { if (err) return next(err); @@ -828,6 +880,8 @@ WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) { paths[id] = path; + self.pathCache.set(hash, paths); + batch.put('W/' + hash, serializePaths(paths)); next(); @@ -840,6 +894,31 @@ WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) { }); }; +WalletDB.prototype._getPaths = function _getPaths(hash, callback) { + var self = this; + var paths; + + if (!hash) + return callback(); + + paths = this.pathCache.get(hash); + + if (paths) + return callback(null, paths); + + this.db.fetch('W/' + hash, parsePaths, function(err, paths) { + if (err) + return callback(err); + + if (!paths) + return callback(); + + self.pathCache.set(hash, paths); + + return callback(null, paths); + }); +}; + /** * Test whether an address hash exists in the * path map and is relevant to the wallet id. @@ -867,10 +946,7 @@ WalletDB.prototype.hasAddress = function hasAddress(id, address, callback) { */ WalletDB.prototype.getAddress = function getAddress(address, callback) { - if (!address) - return callback(); - - this.db.fetch('W/' + address, parsePaths, callback); + this._getPaths(address, callback); }; /** diff --git a/test/http-test.js b/test/http-test.js index 640ec95e..6c5949fa 100644 --- a/test/http-test.js +++ b/test/http-test.js @@ -93,7 +93,7 @@ describe('HTTP', function() { */ it('should fill with funds', function(cb) { - var balance, receive; + var balance, receive, tx; // Coinbase var t1 = bcoin.mtx() @@ -112,10 +112,13 @@ describe('HTTP', function() { receive = r[0]; }); + wallet.once('tx', function(t, map) { + tx = t; + }); + node.walletdb.addTX(t1, function(err) { assert.ifError(err); setTimeout(function() { - return cb(); assert(receive); assert.equal(receive.id, 'test'); assert.equal(receive.type, 'pubkeyhash'); @@ -124,8 +127,10 @@ describe('HTTP', function() { assert.equal(balance.confirmed, 0); assert.equal(balance.unconfirmed, 201840); assert.equal(balance.total, 201840); + assert(tx); + assert.equal(tx.hash('hex'), t1.hash('hex')); cb(); - }, 2000); + }, 300); }); }); @@ -171,6 +176,24 @@ describe('HTTP', function() { }); }); + it('should generate new api key', function(cb) { + var t = wallet.token.toString('hex'); + wallet.retoken(null, function(err, token) { + assert.ifError(err); + assert(token.length === 64); + assert.notEqual(token, t); + cb(); + }); + }); + + it('should get balance', function(cb) { + wallet.getBalance(function(err, balance) { + assert.ifError(err); + assert.equal(balance.total, 199570); + cb(); + }); + }); + it('should cleanup', function(cb) { constants.tx.COINBASE_MATURITY = 100; node.close(cb); diff --git a/test/wallet-test.js b/test/wallet-test.js index 2bce257f..a0d6798b 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -86,10 +86,10 @@ describe('Wallet', function() { w1.destroy(); walletdb.get(w1.id, function(err, w1_) { assert.ifError(err); - assert(w1 !== w1_); - assert(w1.master !== w1_.master); + // assert(w1 !== w1_); + // assert(w1.master !== w1_.master); assert.equal(w1.master.key.xprivkey, w1.master.key.xprivkey); - assert(w1.account !== w1_.account); + // assert(w1.account !== w1_.account); assert.equal(w1.account.accountKey.xpubkey, w1.account.accountKey.xpubkey); cb(); }); @@ -819,8 +819,8 @@ describe('Wallet', function() { assert.ifError(err); assert.equal(account.name, 'foo'); assert.equal(account.accountIndex, 1); - assert(account !== w1.account); - assert(account !== acc); + // assert(account !== w1.account); + // assert(account !== acc); assert(account.accountKey.xpubkey === acc.accountKey.xpubkey); assert(w1.account.accountIndex === 0); assert(account.receiveAddress.getAddress('base58') !== w1.account.receiveAddress.getAddress('base58'));