From e962686e93709cc454a4126356dbd0bc1091c3f8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 3 Mar 2016 19:54:44 -0800 Subject: [PATCH] wallet provider. --- lib/bcoin/txdb.js | 5 -- lib/bcoin/wallet.js | 89 +++++++++------------ lib/bcoin/walletdb.js | 177 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 73 deletions(-) diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index eceaad3b..c5ca6a32 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -86,7 +86,6 @@ TXPool.prototype.getMap = function getMap(tx, callback) { map.input = []; map.output = []; map.all = []; - map.table = {}; tx.getInputAddresses().forEach(function(address) { assert(map[address]); @@ -101,10 +100,6 @@ TXPool.prototype.getMap = function getMap(tx, callback) { map.input = utils.uniqs(map.input); map.output = utils.uniqs(map.output); map.all = utils.uniqs(map.input.concat(map.output)); - map.table = map.all.reduce(function(out, id) { - out[id] = true; - return out; - }, {}); return callback(null, map); } diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index d6642a6a..bd09886a 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -44,8 +44,6 @@ function Wallet(options) { options.master = bcoin.hd.fromSeed(); this.options = options; - this.db = options.db || null; - this.tx = options.tx || null; this.provider = options.provider || null; this.addresses = []; this.master = options.master || null; @@ -131,61 +129,36 @@ Wallet.prototype._init = function _init() { assert(!this.receiveAddress.change); assert(this.changeAddress.change); - if (!this.tx) + if (!this.provider) return; - this.tx.on('tx', this._onTX = function(tx) { - if (!self.ownInput(tx) && !self.ownOutput(tx)) - return; + this.provider.setID(this.id); + this.provider.on('tx', function(tx) { self.emit('tx', tx); }); - this.tx.on('updated', this._onUpdated = function(tx) { - if (!self.ownInput(tx) && !self.ownOutput(tx)) - return; - + this.provider.on('updated', function(tx) { self.emit('updated', tx); - - if (!self.provider) - return; - - self.getBalance(function(err, balance) { - if (err) - return self.emit('error', err); - - self.emit('balance', balance); - }); }); - this.tx.on('confirmed', this._onConfirmed = function(tx) { - if (!self.ownInput(tx) && !self.ownOutput(tx)) - return; + this.provider.on('balance', function(balance) { + self.emit('balance', balance); + }); + this.provider.on('confirmed', function(tx) { self.syncOutputDepth(tx); self.emit('confirmed', tx); }); + + this.provider.on('unconfirmed', function(tx) { + self.syncOutputDepth(tx); + self.emit('unconfirmed', tx); + }); }; Wallet.prototype.destroy = function destroy() { - if (this.tx) { - if (this._onTX) { - this.tx.removeListener('tx', this._onTX); - delete this._onTX; - } - - if (this._onUpdated) { - this.tx.removeListener('updated', this._onUpdated); - delete this._onUpdated; - } - - if (this._onConfirmed) { - this.tx.removeListener('confirmed', this._onConfirmed); - delete this._onConfirmed; - } - } - this.db = null; - this.tx = null; + this.provider.destroy(); this.provider = null; }; @@ -407,8 +380,8 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) { this.addressMap[address.getProgramAddress()] = data.path; // Update the DB with the new address. - if (this.db) - this.db.update(this, address); + if (this.provider && this.provider.update) + this.provider.update(this, address); this.emit('add address', address); @@ -539,7 +512,21 @@ Wallet.prototype.fillPrevout = function fillPrevout(tx, callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.fillCoin(tx, callback); + return this.provider.fillTX(tx, callback); +}; + +Wallet.prototype.getCoin = function getCoin(id, callback) { + if (!this.provider) + return callback(new Error('No wallet provider available.')); + + return this.provider.getCoin(id, callback); +}; + +Wallet.prototype.getTX = function getTX(hash, callback) { + if (!this.provider) + return callback(new Error('No wallet provider available.')); + + return this.provider.getTX(hash, callback); }; Wallet.prototype.createTX = function createTX(options, outputs, callback) { @@ -801,45 +788,45 @@ Wallet.prototype.sign = function sign(tx, type, index) { }; Wallet.prototype.addTX = function addTX(tx, callback) { - if (!this.tx) + if (!this.provider || !this.provider.addTX) return callback(new Error('No transaction pool available.')); - return this.tx.add(tx, callback); + return this.provider.addTX(tx, callback); }; Wallet.prototype.getAll = function getAll(callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.getAll(this, callback); + return this.provider.getAll(callback); }; Wallet.prototype.getCoins = function getCoins(callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.getCoins(this, callback); + return this.provider.getCoins(callback); }; Wallet.prototype.getPending = function getPending(callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.getPending(this, callback); + return this.provider.getPending(callback); }; Wallet.prototype.getBalance = function getBalance(callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.getBalance(this, callback); + return this.provider.getBalance(callback); }; Wallet.prototype.getLast = function getLast(callback) { if (!this.provider) return callback(new Error('No wallet provider available.')); - return this.provider.getLast(this, callback); + return this.provider.getLast(callback); }; Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 767c99a1..faef2fbf 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -125,9 +125,42 @@ WalletDB.prototype._init = function _init() { self.emit('error', err); }); + this.tx.on('tx', function(tx, map) { + map.all.forEach(function(id) { + self.emit(id + ' tx', tx); + }); + }); + + this.tx.on('confirmed', function(tx, map) { + map.all.forEach(function(id) { + self.emit(id + ' confirmed', tx); + }); + }); + + this.tx.on('unconfirmed', function(tx, map) { + map.all.forEach(function(id) { + self.emit(id + ' unconfirmed', tx); + }); + }); + this.tx.on('updated', function(tx, map) { self.emit('wallet tx', tx, map); + utils.forEachSerial(map.output, function(id, next) { + if (self.listeners(id + ' balance').length === 0) + return next(); + + self.getBalance(id, function(err, balance) { + if (err) + return self.emit('error', err); + + self.emit(id + ' balance', balance); + }); + }, function(err) { + if (err) + self.emit('error', err); + }); + // Only sync for confirmed txs. if (tx.ts === 0) return; @@ -145,6 +178,7 @@ WalletDB.prototype._init = function _init() { self.saveJSON(id, json, function(err) { if (err) return next(err); + next(); }); }); @@ -197,7 +231,7 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) { callback = utils.ensure(callback); - function cb(err, json) { + return this._saveDB(id, json, function(err, json) { var batch; if (err) @@ -216,9 +250,7 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) { } return callback(null, json); - } - - return this._saveDB(id, json, cb); + }); }; WalletDB.prototype.removeJSON = function removeJSON(id, callback) { @@ -229,7 +261,7 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) { if (typeof id === 'object') id = id.id; - function cb(err, json) { + return this._removeDB(id, function(err, json) { var batch; if (err) @@ -248,9 +280,7 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) { } return callback(null, json); - } - - return this._removeDB(id, cb); + }); }; WalletDB.prototype._getDB = function _getDB(id, callback) { @@ -334,9 +364,7 @@ WalletDB.prototype.get = function get(id, passphrase, callback) { try { options = bcoin.wallet._fromJSON(options, passphrase); - options.db = self; - options.tx = self.tx; - options.provider = self; + options.provider = new Provider(self); wallet = new bcoin.wallet(options); } catch (e) { return callback(e); @@ -398,18 +426,14 @@ WalletDB.prototype.create = function create(options, callback) { if (json) { try { options = bcoin.wallet._fromJSON(json, options.passphrase); - options.db = self; - options.tx = self.tx; - options.provider = self; + options.provider = new Provider(self); wallet = new bcoin.wallet(options); } catch (e) { return callback(e); } done(); } else { - options.db = self; - options.tx = self.tx; - options.provider = self; + options.provider = new Provider(self); wallet = new bcoin.wallet(options); self.saveJSON(wallet.id, wallet.toJSON(), done); } @@ -535,6 +559,125 @@ WalletDB.prototype.removeBlock = function removeBlock(block, callback) { }, callback); }; +/** + * Provider + */ + +function Provider(db) { + if (!(this instanceof Provider)) + return new Provider(db); + + EventEmitter.call(this); + + this.db = db; + this.id = null; +} + +utils.inherits(Provider, EventEmitter); + +Provider.prototype.setID = function setID(id) { + var self = this; + + assert(!this.id); + + this.id = id; + + this.db.on(id + ' tx', this._onTX = function(tx) { + self.emit('tx', tx); + }); + + this.db.on(id + ' updated', this._onUpdated = function(tx) { + self.emit('updated', tx); + }); + + this.db.on(id + ' confirmed', this._onConfirmed = function(tx) { + self.emit('confirmed', tx); + }); + + this.db.on(id + ' unconfirmed', this._onUnconfirmed = function(tx) { + self.emit('unconfirmed', tx); + }); + + this.db.on(id + ' balance', this._onBalance = function(balance) { + self.emit('balance', balance); + }); +}; + +Provider.prototype.destroy = function destroy() { + if (this.db) { + if (this._onTX) { + this.removeListener(this.id + ' tx', this._onTX); + delete this._onTX; + } + + if (this._onUpdated) { + this.removeListener(this.id + ' updated', this._onUpdated); + delete this._onUpdated; + } + + if (this._onConfirmed) { + this.removeListener(this.id + ' confirmed', this._onConfirmed); + delete this._onConfirmed; + } + + if (this._onUnconfirmed) { + this.removeListener(this.id + ' unconfirmed', this._onUnconfirmed); + delete this._onUnconfirmed; + } + + if (this._onBalance) { + this.removeListener(this.id + ' balance', this._onBalance); + delete this._onBalance; + } + } + + this.db = null; +}; + +Provider.prototype.getAll = function getAll(callback) { + return this.db.getAll(this.id, callback); +}; + +Provider.prototype.getCoins = function getCoins(callback) { + return this.db.getCoins(this.id, callback); +}; + +Provider.prototype.getPending = function getPending(callback) { + return this.db.getPending(this.id, callback); +}; + +Provider.prototype.getBalance = function getBalance(callback) { + return this.db.getBalance(this.id, callback); +}; + +Provider.prototype.getLast = function getLast(callback) { + return this.db.getLast(this.id, callback); +}; + +Provider.prototype.getTX = function getTX(hash, callback) { + return this.db.getTX(hash, callback); +}; + +Provider.prototype.getCoin = function getCoin(hash, index, callback) { + return this.db.getCoin(hash, index, callback); +}; + +Provider.prototype.fillTX = function fillTX(tx, callback) { + return this.db.fillTX(tx, callback); +}; + +Provider.prototype.fillCoin = function fillCoin(tx, callback) { + return this.db.fillCoin(tx, callback); +}; + +Provider.prototype.addTX = function addTX(tx, callback) { + return this.db.tx.add(tx, callback); +}; + +Provider.prototype.update = function update(wallet, address) { + return this.db.update(wallet, address); +}; + /** * Expose */