diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index e3c4c48a..0fea0ed0 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -44,7 +44,7 @@ function KeyRing(options) { this.derived = !!options.derived; this.addressMap = null; - this.key = options.key || bcoin.keypair(options); + this.key = options.key; this.path = options.path; this.change = !!options.change; this.index = options.index; @@ -122,16 +122,6 @@ KeyRing.prototype.removeKey = function removeKey(key) { this.keys = utils.sortKeys(this.keys); }; -/** - * Get private key. - * @param {String?} enc - `"hex"` or `null`. - * @returns {Buffer} - */ - -KeyRing.prototype.getPrivateKey = function getPrivateKey(enc) { - return this.key.getPrivateKey(enc); -}; - /** * Get public key. * @param {String?} enc - `"hex"` or `null`. @@ -461,45 +451,6 @@ KeyRing.prototype.scriptInputs = function scriptInputs(tx, index) { return total; }; -/** - * Sign inputs for a transaction. Only attempts to sign inputs - * that are redeemable by this address. - * @param {MTX} tx - * @param {Number?} index - Index of input. If not present, - * it will attempt to sign all redeemable inputs. - * @param {SighashType?} type - * @returns {Number} Total number of inputs signed. - */ - -KeyRing.prototype.signInputs = function signInputs(tx, index, type) { - var total = 0; - var i, input; - - if (index && typeof index === 'object') - index = tx.inputs.indexOf(index); - - if (!this.key.privateKey) - return 0; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - - if (index != null && index !== i) - continue; - - if (!input.coin) - continue; - - if (!this.ownOutput(input.coin)) - continue; - - if (tx.signInput(i, this, type)) - total++; - } - - return total; -}; - /** * Build input scripts and sign inputs for a transaction. Only attempts * to build/sign inputs that are redeemable by this address. @@ -510,16 +461,13 @@ KeyRing.prototype.signInputs = function signInputs(tx, index, type) { * @returns {Number} Total number of inputs scripts built and signed. */ -KeyRing.prototype.sign = function sign(tx, index, type) { +KeyRing.prototype.sign = function sign(tx, key, index, type) { var total = 0; var i, input; if (index && typeof index === 'object') index = tx.inputs.indexOf(index); - if (!this.key.privateKey) - return 0; - for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -532,7 +480,7 @@ KeyRing.prototype.sign = function sign(tx, index, type) { if (!this.ownOutput(input.coin)) continue; - if (tx.sign(i, this, type)) + if (tx.sign(i, this, key, type)) total++; } @@ -595,67 +543,6 @@ KeyRing.prototype.__defineGetter__('address', function() { return this.getAddress(); }); -/** - * Convert an KeyRing to a more json-friendly object. - * @param {String?} passphrase - KeyRing passphrase - * @returns {Object} - */ - -KeyRing.prototype.toJSON = function toJSON(passphrase) { - var key = this.key; - - if (!(key instanceof bcoin.keypair)) - key = new bcoin.keypair({ privateKey: key.getPrivateKey() }); - - return { - v: 1, - name: 'address', - network: this.network.type, - label: this.label, - change: this.change, - derived: this.derived, - index: this.index, - path: this.path, - address: this.getAddress(), - key: key.toJSON(passphrase), - type: this.type, - witness: this.witness, - keys: this.keys.map(utils.toBase58), - m: this.m, - n: this.n - }; -}; - -/** - * Instantiate an KeyRing from a jsonified transaction object. - * @param {Object} json - The jsonified transaction object. - * @param {String?} passphrase - KeyRing passphrase - * @returns {KeyRing} - */ - -KeyRing.fromJSON = function fromJSON(json, passphrase) { - var w; - - assert.equal(json.v, 1); - assert.equal(json.name, 'address'); - - w = new KeyRing({ - label: json.label, - change: json.change, - derived: json.derived, - index: json.index, - path: json.path, - key: bcoin.keypair.fromJSON(json.key, passphrase), - type: json.type, - witness: json.witness, - keys: json.keys.map(utils.fromBase58), - m: json.m, - n: json.n - }); - - return w; -}; - /* * Expose */ diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index e108c0cd..bcac1208 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -360,7 +360,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type, * @throws on unavailable coins. */ -MTX.prototype.signInput = function signInput(index, addr, type) { +MTX.prototype.signInput = function signInput(index, addr, key, type) { var input, prev, signature, keyIndex, signatures, i; var len, m, n, keys, vector, dummy, version; @@ -410,7 +410,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { } // Create our signature. - signature = this.createSignature(index, prev, addr.key, type, version); + signature = this.createSignature(index, prev, key, type, version); // P2PK if (prev.isPubkey()) { @@ -630,7 +630,7 @@ MTX.prototype.isSigned = function isSigned() { * @throws on unavailable coins. */ -MTX.prototype.sign = function sign(index, addr, type) { +MTX.prototype.sign = function sign(index, addr, key, type) { var input; if (index && typeof index === 'object') @@ -644,7 +644,7 @@ MTX.prototype.sign = function sign(index, addr, type) { return false; // Sign input - if (!this.signInput(index, addr, type)) + if (!this.signInput(index, addr, key, type)) return false; return true; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 8a16768c..bf05bf5e 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -17,7 +17,7 @@ var BufferWriter = require('./writer'); * @exports Wallet * @constructor * @param {Object} options - * @param {Provider?} options.provider - Wallet Provider. If not + * @param {Provider} options.provider * present, no coins will be available. * @param {(HDPrivateKey|HDPublicKey)?} options.master - Master HD key. If not * present, it will be generated. @@ -45,8 +45,46 @@ var BufferWriter = require('./writer'); * (default=account key "address"). */ +function MasterKey(options) { + this.json = options.json; + this.key = options.key || null; +} + +MasterKey.prototype.decrypt = function decrypt(passphrase) { + if (this.key) + return this.key; + + if (!this.json.encrypted) + return bcoin.hd.fromJSON(this.json); + + return bcoin.hd.fromJSON(this.json, passphrase); +}; + +MasterKey.prototype.toJSON = function toJSON() { + return this.json; +}; + +MasterKey.fromKey = function fromKey(key) { + return new MasterKey({ + key: key, + json: key.toJSON() + }); +}; + +MasterKey.fromJSON = function fromJSON(json) { + var key; + + if (!json.encrypted) + key = bcoin.hd.fromJSON(json); + + return new MasterKey({ + key: key, + json: json + }); +}; + function Wallet(options) { - var i; + var i, key; if (!(this instanceof Wallet)) return new Wallet(options); @@ -61,20 +99,18 @@ function Wallet(options) { this.options = options; this.network = bcoin.network.get(options.network); - if (typeof options.master === 'string') - options.master = { xkey: options.master }; - - if (options.master - && typeof options.master === 'object' - && !(options.master instanceof bcoin.hd)) { - options.master = bcoin.hd.fromAny(options.master, this.network); - } - if (!options.master) options.master = bcoin.hd.fromMnemonic(null, this.network); + if (!(options.master instanceof bcoin.hd)) + options.master = bcoin.hd.fromAny(options.master, this.network); + + options.master = MasterKey.fromKey(options.master); + this.provider = options.provider || null; + this.id = options.id || null; this.master = options.master || null; + this.accountKey = options.accountKey || null; this.addressMap = options.addressMap || {}; this.witness = options.witness || false; this.loaded = false; @@ -104,17 +140,21 @@ function Wallet(options) { if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); - if (this.derivation === 'bip45') { - this.accountKey = this.master.isPurpose45() - ? this.master - : this.master.derivePurpose45(); - } else if (this.derivation === 'bip44') { - this.accountKey = this.master.isAccount44() - ? this.master - : this.master.deriveAccount44(this.accountIndex); + if (!this.accountKey) { + key = this.master.key; + + assert(key); + + if (this.derivation === 'bip45') + key = key.derivePurpose45().hdPublicKey; + else if (this.derivation === 'bip44') + key = key.deriveAccount44(this.accountIndex).hdPublicKey; + + this.accountKey = key; } - this.id = this.getID(); + if (!this.id) + this.id = this.getID(); // Non-alphanumeric IDs will break leveldb sorting. assert(/^[a-zA-Z0-9]+$/.test(this.id), 'Wallet IDs must be alphanumeric.'); @@ -129,6 +169,25 @@ function Wallet(options) { utils.inherits(Wallet, EventEmitter); +Wallet.prototype.fromMnemonic = function fromMnemonic(network) { + var master = bcoin.hd.fromMnemonic(null, network); + return Wallet.fromMaster(master, network); +}; + +Wallet.prototype.fromMaster = function fromMaster(master, network) { + if (!master) + master = bcoin.hd.generate(); + + return new Wallet({ master: master, network: network }); +}; + +Wallet.prototype.fromAccount = function fromAccount(accountKey, network) { + if (!master) + master = bcoin.hd.generate(); + + return new Wallet({ accountKey: accountKey, network: network }); +}; + Wallet.prototype._init = function _init() { var self = this; var i; @@ -241,6 +300,12 @@ Wallet.prototype.addKey = function addKey(key) { assert(key, 'Key required.'); + if (Array.isArray(key)) { + for (i = 0; i < key.length; i++) + this.addKey(key[i]); + return; + } + if (key instanceof bcoin.wallet) { assert(key.derivation === this.derivation); key = key.accountKey; @@ -292,6 +357,12 @@ Wallet.prototype.removeKey = function removeKey(key) { assert(!this._keysFinalized); + if (Array.isArray(key)) { + for (i = 0; i < key.length; i++) + this.removeKey(key[i]); + return; + } + assert(key, 'Key required.'); if (key instanceof bcoin.wallet) { @@ -414,11 +485,6 @@ Wallet.prototype.createAddress = function createAddress(change) { this.receiveAddress = address; } - if (this.provider && this.provider.sync) { - assert(!this.provider.update); - this.provider.sync(this, address); - } - return address; }; @@ -517,10 +583,6 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) { if (this.witness) this.addressMap[address.getProgramHash('hex')] = data.path; - // Update the DB with the new address. - if (this.provider && this.provider.update) - this.provider.update(this, address); - this.emit('add address', address); this.cache.set(data.path, address); @@ -834,10 +896,6 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) { if (!tx.checkInputs(height)) return callback(new Error('CheckInputs failed.')); - // Sign the transaction - if (!self.sign(tx)) - return callback(new Error('Could not sign transaction.')); - return callback(null, tx); }); }; @@ -1180,27 +1238,6 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, index) { return total; }; -/** - * Sign inputs for a transaction. Only attempts to sign inputs - * that are redeemable by this wallet. - * @param {MTX} tx - * @param {Number?} index - Index of input. If not present, - * it will attempt to sign all redeemable inputs. - * @param {SighashType?} type - * @returns {Number} Total number of inputs signed. - */ - -Wallet.prototype.signInputs = function signInputs(tx, index, type) { - var addresses = this.deriveInputs(tx, index); - var total = 0; - var i; - - for (i = 0; i < addresses.length; i++) - total += addresses[i].signInputs(tx, index, type); - - return total; -}; - /** * Build input scripts and sign inputs for a transaction. Only attempts * to build/sign inputs that are redeemable by this wallet. @@ -1211,13 +1248,24 @@ Wallet.prototype.signInputs = function signInputs(tx, index, type) { * @returns {Number} Total number of inputs scripts built and signed. */ -Wallet.prototype.sign = function sign(tx, index, type) { +Wallet.prototype.sign = function sign(tx, passphrase, index, type) { var addresses = this.deriveInputs(tx, index); + var key = this.master.decrypt(passphrase); var total = 0; - var i; + var i, address, key; - for (i = 0; i < addresses.length; i++) - total += addresses[i].sign(tx, index, type); + for (i = 0; i < addresses.length; i++) { + address = addresses[i]; + + if (this.derivation === 'bip44') + key = key.deriveAccount44(this.accountIndex); + else if (this.derivation === 'bip45') + key = key.derivePurpose45(); + + key = key.derive(address.path); + + total += address.sign(tx, key, index, type); + } return total; }; @@ -1325,16 +1373,6 @@ Wallet.prototype.getTimeRange = function getTimeRange(options, callback) { return this.provider.getTimeRange(options, callback); }; -/** - * Get private key for current receiving address. - * @param {String?} enc - `"hex"` or `null`. - * @returns {Buffer} - */ - -Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { - return this.receiveAddress.getPrivateKey(enc); -}; - /** * Get public key for current receiving address. * @param {String?} enc - `"hex"` or `null`. @@ -1575,7 +1613,7 @@ Wallet.prototype.toJSON = function toJSON() { accountIndex: this.accountIndex, receiveDepth: this.receiveDepth, changeDepth: this.changeDepth, - master: this.master.toJSON(this.options.passphrase), + master: this.master.toJSON(), accountKey: this.accountKey.xpubkey, addressMap: this.addressMap, keys: this.keys.map(function(key) { @@ -1595,7 +1633,7 @@ Wallet.prototype.toJSON = function toJSON() { * @throws Error on bad decrypt */ -Wallet.parseJSON = function parseJSON(json, passphrase) { +Wallet.parseJSON = function parseJSON(json) { assert.equal(json.v, 3); assert.equal(json.name, 'wallet'); @@ -1611,10 +1649,12 @@ Wallet.parseJSON = function parseJSON(json, passphrase) { accountIndex: json.accountIndex, receiveDepth: json.receiveDepth, changeDepth: json.changeDepth, - master: bcoin.hd.fromJSON(json.master, passphrase), + master: MasterKey.fromJSON(json.master), + accountKey: bcoin.hd.fromBase58(json.accountKey), addressMap: json.addressMap, - keys: json.keys, - passphrase: passphrase + keys: json.keys.map(function(key) { + return bcoin.hd.fromBase58(key); + }) }; }; @@ -1622,160 +1662,11 @@ Wallet.parseJSON = function parseJSON(json, passphrase) { * Instantiate a Wallet from a * jsonified wallet object. * @param {Object} json - The jsonified wallet object. - * @param {String?} passphrase * @returns {Wallet} */ -Wallet.fromJSON = function fromJSON(json, passphrase) { - return new Wallet(Wallet.parseJSON(json, passphrase)); -}; - -Wallet._syncDepth = function _syncDepth(json, options) { - var master, wallet; - var res = false; - - assert.equal(json.v, 3); - assert.equal(json.name, 'wallet'); - - master = json.master; - json.master = json.accountKey; - wallet = new Wallet(json); - - if (!wallet._initialized) - return; - - if (options.tx != null) { - if (wallet.syncOutputDepth(options.tx)) - res = true; - } - - if (options.receiveDepth != null) { - if (wallet.setReceiveDepth(options.receiveDepth)) - res = true; - } - - if (options.changeDepth != null) { - if (wallet.setChangeDepth(options.changeDepth)) - res = true; - } - - if (!res) - return; - - wallet = wallet.toJSON(); - wallet.master = master; - - return wallet; -}; - -/** - * Sync output depth of a jsonified wallet object. - * Useful for increasing depth without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {TX} tx - * @returns {json} - */ - -Wallet.syncOutputDepth = function syncOutputDepth(json, tx) { - return Wallet._syncDepth(json, { tx: tx }); -}; - -/** - * Increase receive depth of a jsonified wallet object. - * Useful for increasing depth without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {Number} receiveDepth - * @returns {json} - */ - -Wallet.setReceiveDepth = function setReceiveDepth(json, receiveDepth) { - return Wallet._syncDepth(json, { - receiveDepth: receiveDepth || 0 - }); -}; - -/** - * Increase change depth of a jsonified wallet object. - * Useful for increasing depth without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {Number} changeDepth - * @returns {json} - */ - -Wallet.setChangeDepth = function setChangeDepth(json, changeDepth) { - return Wallet._syncDepth(json, { - changeDepth: changeDepth || 0 - }); -}; - -/** - * Increase depths of a jsonified wallet object. - * Useful for increasing depth without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {Number} receiveDepth - * @param {Number} changeDepth - * @returns {json} - */ - -Wallet.setDepth = function setDepth(json, receiveDepth, changeDepth) { - return Wallet._syncDepth(json, { - receiveDepth: receiveDepth || 0, - changeDepth: changeDepth || 0 - }); -}; - -Wallet._addKey = function _addKey(json, keys, remove) { - var master, wallet; - - assert.equal(json.v, 3); - assert.equal(json.name, 'wallet'); - - if (!Array.isArray(keys)) - keys = [keys]; - - master = json.master; - json.master = json.accountKey; - wallet = new Wallet(json); - keys.forEach(function(key) { - if (remove) - wallet.removeKey(key); - else - wallet.addKey(key); - }); - wallet = wallet.toJSON(); - wallet.master = master; - - return wallet; -}; - -/** - * Add keys to a jsonified wallet object. - * Useful for managing keys without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {Buffer[]|Base58String[]} keys - * @returns {json} - */ - -Wallet.addKey = function addKey(json, keys) { - return Wallet._addKey(json, keys, false); -}; - -/** - * Remove keys from a jsonified wallet object. - * Useful for managing keys without decrypting - * the key and instantiating the wallet. - * @param {json} - * @param {Buffer[]|Base58String[]} keys - * @returns {json} - */ - -Wallet.removeKey = function removeKey(json, keys) { - return Wallet._addKey(json, keys, true); +Wallet.fromJSON = function fromJSON(json) { + return new Wallet(Wallet.parseJSON(json)); }; /** diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index b71afc08..a866cf55 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -232,17 +232,16 @@ WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) callback = utils.ensure(callback); - this.getJSON(id, function(err, json) { + this.get(id, function(err, wallet) { if (err) return callback(err); - // Allocate new addresses if necessary. - json = bcoin.wallet.syncOutputDepth(json, tx); + if (!wallet) + return callback(new Error('No wallet.')); - if (!json) - return callback(); + wallet.syncOutputDepth(tx); - self.saveJSON(id, json, function(err) { + self.save(wallet, function(err) { if (err) return callback(err); @@ -253,42 +252,6 @@ WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) }); }; -/** - * Set receiving/change depth (depth is the index of the _next_ address). - * Allocate all addresses up to depth. Note that this also allocates - * new lookahead addresses. - * @param {WalletID} id - * @param {Number} receive - Receive address depth. - * @param {Number} change - Change address depth. - * @param {Function} callback - */ - -WalletDB.prototype.setDepth = function setDepth(id, receive, change, callback) { - var self = this; - - callback = utils.ensure(callback); - - this.getJSON(id, function(err, json) { - if (err) - return callback(err); - - // Allocate new addresses if necessary. - json = bcoin.wallet.setDepth(json, receive, change); - - if (!json) - return callback(); - - self.saveJSON(id, json, function(err) { - if (err) - return callback(err); - - self.emit('set depth', id, receive, change); - - callback(); - }); - }); -}; - /** * Add a public account/purpose key to the wallet for multisig. * @param {WalletID} id @@ -302,17 +265,20 @@ WalletDB.prototype.addKey = function addKey(id, key, callback) { callback = utils.ensure(callback); - this.getJSON(id, function(err, json) { + this.get(id, function(err, json) { if (err) return callback(err); + if (!wallet) + return callback(new Error('No wallet.')); + try { - json = bcoin.wallet.addKey(json, key); + wallet.addKey(key); } catch (e) { return callback(e); } - self.saveJSON(id, json, callback); + self.save(wallet, callback); }); }; @@ -329,35 +295,23 @@ WalletDB.prototype.removeKey = function removeKey(id, key, callback) { callback = utils.ensure(callback); - this.getJSON(id, function(err, json) { + this.get(id, function(err, wallet) { if (err) return callback(err); + if (!wallet) + return callback(new Error('No wallet.')); + try { - json = bcoin.wallet.removeKey(json, key); + wallet.removeKey(key); } catch (e) { return callback(e); } - self.saveJSON(id, json, callback); + self.save(wallet, callback); }); }; -/** - * Retrieve wallet without instantiating it. - * @param {WalletID} id - * @param {Function} callback - Returns [Error, Object(nakedWallet)]. - */ - -WalletDB.prototype.getJSON = function getJSON(id, callback) { - if (typeof id === 'object') - id = id.id; - - callback = utils.ensure(callback); - - return this._getDB(id, callback); -}; - /** * Save a "naked" (non-instantiated) wallet. Will * also index the address table. @@ -368,33 +322,27 @@ WalletDB.prototype.getJSON = function getJSON(id, callback) { WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) { var self = this; + var data = new Buffer(JSON.stringify(json), 'utf8'); + var batch; - callback = utils.ensure(callback); - - return this._saveDB(id, json, function(err, json) { - var batch; - + this.db.put('w/' + id, data, function(err) { if (err) return callback(err); - if (json) { - batch = self.db.batch(); + batch = self.db.batch(); - Object.keys(json.addressMap).forEach(function(address) { - if (self.tx.filter) - self.tx.filter.add(address, 'hex'); + Object.keys(json.addressMap).forEach(function(address) { + if (self.tx.filter) + self.tx.filter.add(address, 'hex'); - batch.put('W/' + address + '/' + json.id, DUMMY); - }); + batch.put('W/' + address + '/' + json.id, DUMMY); + }); - return batch.write(function(err) { - if (err) - return callback(err); - return callback(null, json); - }); - } - - return callback(null, json); + return batch.write(function(err) { + if (err) + return callback(err); + return callback(null, json); + }); }); }; @@ -406,31 +354,31 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) { WalletDB.prototype.removeJSON = function removeJSON(id, callback) { var self = this; + var batch; callback = utils.ensure(callback); - if (typeof id === 'object') - id = id.id; - - return this._removeDB(id, function(err, json) { - var batch; - + this.getJSON(id, function(err, json) { if (err) return callback(err); - if (json) { - batch = self.db.batch(); - Object.keys(json.addressMap).forEach(function(address) { - batch.del('W/' + address + '/' + json.id); - }); - return batch.write(function(err) { - if (err) + batch = self.db.batch(); + + Object.keys(json.addressMap).forEach(function(address) { + batch.del('W/' + address + '/' + json.id); + }); + + batch.write(function(err) { + if (err) + return callback(err); + + self.db.del(key, function(err) { + if (err && err.type !== 'NotFoundError') return callback(err); + return callback(null, json); }); - } - - return callback(null, json); + }); }); }; @@ -441,12 +389,10 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) { * @param {Function} callback - Returns [Error, Object(nakedWallet)]. */ -WalletDB.prototype._getDB = function _getDB(id, callback) { - var key = 'w/' + id; - +WalletDB.prototype.getJSON = function getJSON(id, callback) { callback = utils.ensure(callback); - this.db.get(key, function(err, json) { + this.db.get('w/' + id, function(err, json) { if (err && err.type === 'NotFoundError') return callback(); @@ -463,71 +409,15 @@ WalletDB.prototype._getDB = function _getDB(id, callback) { }); }; -/** - * Save object to the database. - * @private - * @param {WalletID} id - * @param {Object} json - * @param {Function} callback - Returns [Error, nakedWallet]. - */ - -WalletDB.prototype._saveDB = function _saveDB(id, json, callback) { - var key = 'w/' + id; - var data; - - callback = utils.ensure(callback); - - data = new Buffer(JSON.stringify(json), 'utf8'); - - this.db.put(key, data, function(err) { - if (err) - return callback(err); - - return callback(null, json); - }); -}; - -/** - * Remove object from the database. - * @private - * @param {WalletID} id - * @param {Function} callback - Returns [Error, Object]. - */ - -WalletDB.prototype._removeDB = function _removeDB(id, callback) { - var self = this; - var key = 'w/' + id; - - callback = utils.ensure(callback); - - this._getDB(id, function(err, json) { - if (err) - return callback(err); - - self.db.del(key, function(err) { - if (err && err.type !== 'NotFoundError') - return callback(err); - - return callback(null, json); - }); - }); -}; - /** * Get a wallet from the database, instantiate, decrypt, and setup provider. * @param {WalletID} id - * @param {String?} passphrase * @param {Function} callback - Returns [Error, {@link Wallet}]. */ -WalletDB.prototype.get = function get(id, passphrase, callback) { +WalletDB.prototype.get = function get(id, callback) { var self = this; - if (typeof passphrase === 'function') { - callback = passphrase; - passphrase = null; - } - callback = utils.ensure(callback); return this.getJSON(id, function(err, options) { @@ -540,7 +430,7 @@ WalletDB.prototype.get = function get(id, passphrase, callback) { return callback(); try { - options = bcoin.wallet.parseJSON(options, passphrase); + options = bcoin.wallet.parseJSON(options); options.provider = new Provider(self); wallet = new bcoin.wallet(options); } catch (e) { @@ -558,24 +448,8 @@ WalletDB.prototype.get = function get(id, passphrase, callback) { * @param {Function} callback */ -WalletDB.prototype.save = function save(id, options, callback) { - if (id && typeof id === 'object') { - callback = options; - options = id; - id = null; - } - - if (!id) - id = options.id; - else - options.id = id; - - callback = utils.ensure(callback); - - assert(options instanceof bcoin.wallet); - options = options.toJSON(); - - this.saveJSON(id, options, callback); +WalletDB.prototype.save = function save(wallet, callback) { + this.saveJSON(wallet.id, wallet.toJSON(), callback); }; /** @@ -585,14 +459,6 @@ WalletDB.prototype.save = function save(id, options, callback) { */ WalletDB.prototype.remove = function remove(id, callback) { - if (id instanceof bcoin.wallet) - id.destroy(); - - if (id && id.id) - id = id.id; - - callback = utils.ensure(callback); - return this.removeJSON(id, callback); }; @@ -603,30 +469,21 @@ WalletDB.prototype.remove = function remove(id, callback) { * @param {Function} callback - Returns [Error, {@link Wallet}]. */ -WalletDB.prototype.create = function create(id, options, callback) { +WalletDB.prototype.create = function create(options, callback) { var self = this; - if (id && typeof id === 'object') { - callback = options; - options = id; - id = null; - } - - if (!id) - id = options.id; - else - options.id = id; - - callback = utils.ensure(callback); - function create(err, json) { var wallet; if (err) return callback(err); - if (json) - return callback(new Error('`' + id + '` already exists.'), null, json); + if (json) { + return callback( + new Error('`' + options.id + '` already exists.'), + null, + json); + } if (self.network.witness) options.witness = options.witness !== false; @@ -635,7 +492,7 @@ WalletDB.prototype.create = function create(id, options, callback) { options.network = self.network; wallet = new bcoin.wallet(options); - self.saveJSON(wallet.id, wallet.toJSON(), function(err) { + self.save(wallet, function(err) { if (err) return callback(err); @@ -643,10 +500,10 @@ WalletDB.prototype.create = function create(id, options, callback) { }); } - if (!id) + if (!options.id) return create(); - return this.getJSON(id, create); + return this.getJSON(options.id, create); }; /** @@ -656,23 +513,9 @@ WalletDB.prototype.create = function create(id, options, callback) { * @param {Function} callback */ -WalletDB.prototype.ensure = function ensure(id, options, callback) { +WalletDB.prototype.ensure = function ensure(options, callback) { var self = this; - - if (id && typeof id === 'object') { - callback = options; - options = id; - id = null; - } - - if (!id) - id = options.id; - else - options.id = id; - - callback = utils.ensure(callback); - - return this.create(id, options, function(err, wallet, json) { + return this.create(options, function(err, wallet, json) { if (err && !json) return callback(err); @@ -682,7 +525,7 @@ WalletDB.prototype.ensure = function ensure(id, options, callback) { assert(json); try { - options = bcoin.wallet.parseJSON(json, options.passphrase); + options = bcoin.wallet.parseJSON(json); options.provider = new Provider(self); wallet = new bcoin.wallet(options); } catch (e) { @@ -693,60 +536,6 @@ WalletDB.prototype.ensure = function ensure(id, options, callback) { }); }; -/** - * Notify the database that a new address - * has been derived. Save to address table. Save wallet. - * @param {Wallet} wallet - * @param {Address} address - */ - -WalletDB.prototype.update = function update(wallet, address) { - var self = this; - var batch; - - // Ugly hack to avoid extra writes. - if (!wallet.changeAddress && wallet.changeDepth > 1) - return; - - batch = this.db.batch(); - - batch.put( - 'W/' + address.getKeyHash('hex') + '/' + wallet.id, - DUMMY); - - if (this.tx.filter) - this.tx.filter.add(address.getKeyHash()); - - if (address.type === 'multisig') { - batch.put( - 'W/' + address.getScriptHash('hex') + '/' + wallet.id, - DUMMY); - - if (this.tx.filter) - this.tx.filter.add(address.getScriptHash()); - } - - if (address.witness) { - batch.put( - 'W/' + address.getProgramHash('hex') + '/' + wallet.id, - DUMMY); - - if (this.tx.filter) - this.tx.filter.add(address.getProgramHash()); - } - - batch.write(function(err) { - if (err) - self.emit('error', err); - - // XXX might have to encrypt key - slow - self._saveDB(wallet.id, wallet.toJSON(), function(err) { - if (err) - self.emit('error', err); - }); - }); -}; - /** * @see {@link TXDB#add}. */ @@ -776,7 +565,6 @@ WalletDB.prototype.getCoin = function getCoin(hash, index, callback) { */ WalletDB.prototype.getHistory = function getHistory(id, callback) { - id = id.id || id; return this.tx.getHistoryByAddress(id, callback); }; @@ -785,7 +573,6 @@ WalletDB.prototype.getHistory = function getHistory(id, callback) { */ WalletDB.prototype.getCoins = function getCoins(id, callback) { - id = id.id || id; return this.tx.getCoinsByAddress(id, callback); }; @@ -794,7 +581,6 @@ WalletDB.prototype.getCoins = function getCoins(id, callback) { */ WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) { - id = id.id || id; return this.tx.getUnconfirmedByAddress(id, callback); }; @@ -803,7 +589,6 @@ WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) { */ WalletDB.prototype.getBalance = function getBalance(id, callback) { - id = id.id || id; return this.tx.getBalanceByAddress(id, callback); }; @@ -812,7 +597,6 @@ WalletDB.prototype.getBalance = function getBalance(id, callback) { */ WalletDB.prototype.getLastTime = function getLastTime(id, callback) { - id = id.id || id; return this.tx.getLastTime(id, callback); }; @@ -821,7 +605,6 @@ WalletDB.prototype.getLastTime = function getLastTime(id, callback) { */ WalletDB.prototype.getLast = function getLast(id, limit, callback) { - id = id.id || id; return this.tx.getLast(id, limit, callback); }; @@ -830,7 +613,6 @@ WalletDB.prototype.getLast = function getLast(id, limit, callback) { */ WalletDB.prototype.getRange = function getRange(id, options, callback) { - id = id.id || id; return this.tx.getRange(id, options, callback); }; @@ -904,7 +686,6 @@ WalletDB.prototype.zap = function zap(now, age, callback) { */ WalletDB.prototype.zapWallet = function zapWallet(id, now, age, callback) { - id = id.id || id; return this.tx.zap(id, now, age, callback); }; @@ -1174,8 +955,8 @@ Provider.prototype.addTX = function addTX(tx, callback) { * @param {Address} address */ -Provider.prototype.update = function update(wallet, address) { - return this.db.update(wallet, address); +Provider.prototype.save = function save(wallet, callback) { + return this.db.save(wallet, callback); }; /**