drop provider from wallet.

This commit is contained in:
Christopher Jeffrey 2016-05-27 01:45:41 -07:00
parent b7bc1cfe77
commit 9c747cde42
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 190 additions and 631 deletions

View File

@ -44,7 +44,7 @@ function KeyRing(options) {
this.derived = !!options.derived; this.derived = !!options.derived;
this.addressMap = null; this.addressMap = null;
this.key = options.key || bcoin.keypair(options); this.key = options.key;
this.path = options.path; this.path = options.path;
this.change = !!options.change; this.change = !!options.change;
this.index = options.index; this.index = options.index;
@ -122,16 +122,6 @@ KeyRing.prototype.removeKey = function removeKey(key) {
this.keys = utils.sortKeys(this.keys); 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. * Get public key.
* @param {String?} enc - `"hex"` or `null`. * @param {String?} enc - `"hex"` or `null`.
@ -461,45 +451,6 @@ KeyRing.prototype.scriptInputs = function scriptInputs(tx, index) {
return total; 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 * Build input scripts and sign inputs for a transaction. Only attempts
* to build/sign inputs that are redeemable by this address. * 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. * @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 total = 0;
var i, input; var i, input;
if (index && typeof index === 'object') if (index && typeof index === 'object')
index = tx.inputs.indexOf(index); index = tx.inputs.indexOf(index);
if (!this.key.privateKey)
return 0;
for (i = 0; i < tx.inputs.length; i++) { for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i]; input = tx.inputs[i];
@ -532,7 +480,7 @@ KeyRing.prototype.sign = function sign(tx, index, type) {
if (!this.ownOutput(input.coin)) if (!this.ownOutput(input.coin))
continue; continue;
if (tx.sign(i, this, type)) if (tx.sign(i, this, key, type))
total++; total++;
} }
@ -595,67 +543,6 @@ KeyRing.prototype.__defineGetter__('address', function() {
return this.getAddress(); 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 * Expose
*/ */

View File

@ -360,7 +360,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type,
* @throws on unavailable coins. * @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 input, prev, signature, keyIndex, signatures, i;
var len, m, n, keys, vector, dummy, version; var len, m, n, keys, vector, dummy, version;
@ -410,7 +410,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) {
} }
// Create our signature. // Create our signature.
signature = this.createSignature(index, prev, addr.key, type, version); signature = this.createSignature(index, prev, key, type, version);
// P2PK // P2PK
if (prev.isPubkey()) { if (prev.isPubkey()) {
@ -630,7 +630,7 @@ MTX.prototype.isSigned = function isSigned() {
* @throws on unavailable coins. * @throws on unavailable coins.
*/ */
MTX.prototype.sign = function sign(index, addr, type) { MTX.prototype.sign = function sign(index, addr, key, type) {
var input; var input;
if (index && typeof index === 'object') if (index && typeof index === 'object')
@ -644,7 +644,7 @@ MTX.prototype.sign = function sign(index, addr, type) {
return false; return false;
// Sign input // Sign input
if (!this.signInput(index, addr, type)) if (!this.signInput(index, addr, key, type))
return false; return false;
return true; return true;

View File

@ -17,7 +17,7 @@ var BufferWriter = require('./writer');
* @exports Wallet * @exports Wallet
* @constructor * @constructor
* @param {Object} options * @param {Object} options
* @param {Provider?} options.provider - Wallet Provider. If not * @param {Provider} options.provider
* present, no coins will be available. * present, no coins will be available.
* @param {(HDPrivateKey|HDPublicKey)?} options.master - Master HD key. If not * @param {(HDPrivateKey|HDPublicKey)?} options.master - Master HD key. If not
* present, it will be generated. * present, it will be generated.
@ -45,8 +45,46 @@ var BufferWriter = require('./writer');
* (default=account key "address"). * (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) { function Wallet(options) {
var i; var i, key;
if (!(this instanceof Wallet)) if (!(this instanceof Wallet))
return new Wallet(options); return new Wallet(options);
@ -61,20 +99,18 @@ function Wallet(options) {
this.options = options; this.options = options;
this.network = bcoin.network.get(options.network); 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) if (!options.master)
options.master = bcoin.hd.fromMnemonic(null, this.network); 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.provider = options.provider || null;
this.id = options.id || null;
this.master = options.master || null; this.master = options.master || null;
this.accountKey = options.accountKey || null;
this.addressMap = options.addressMap || {}; this.addressMap = options.addressMap || {};
this.witness = options.witness || false; this.witness = options.witness || false;
this.loaded = false; this.loaded = false;
@ -104,17 +140,21 @@ function Wallet(options) {
if (this.m < 1 || this.m > this.n) if (this.m < 1 || this.m > this.n)
throw new Error('m ranges between 1 and n'); throw new Error('m ranges between 1 and n');
if (this.derivation === 'bip45') { if (!this.accountKey) {
this.accountKey = this.master.isPurpose45() key = this.master.key;
? this.master
: this.master.derivePurpose45(); assert(key);
} else if (this.derivation === 'bip44') {
this.accountKey = this.master.isAccount44() if (this.derivation === 'bip45')
? this.master key = key.derivePurpose45().hdPublicKey;
: this.master.deriveAccount44(this.accountIndex); 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. // Non-alphanumeric IDs will break leveldb sorting.
assert(/^[a-zA-Z0-9]+$/.test(this.id), 'Wallet IDs must be alphanumeric.'); assert(/^[a-zA-Z0-9]+$/.test(this.id), 'Wallet IDs must be alphanumeric.');
@ -129,6 +169,25 @@ function Wallet(options) {
utils.inherits(Wallet, EventEmitter); 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() { Wallet.prototype._init = function _init() {
var self = this; var self = this;
var i; var i;
@ -241,6 +300,12 @@ Wallet.prototype.addKey = function addKey(key) {
assert(key, 'Key required.'); 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) { if (key instanceof bcoin.wallet) {
assert(key.derivation === this.derivation); assert(key.derivation === this.derivation);
key = key.accountKey; key = key.accountKey;
@ -292,6 +357,12 @@ Wallet.prototype.removeKey = function removeKey(key) {
assert(!this._keysFinalized); assert(!this._keysFinalized);
if (Array.isArray(key)) {
for (i = 0; i < key.length; i++)
this.removeKey(key[i]);
return;
}
assert(key, 'Key required.'); assert(key, 'Key required.');
if (key instanceof bcoin.wallet) { if (key instanceof bcoin.wallet) {
@ -414,11 +485,6 @@ Wallet.prototype.createAddress = function createAddress(change) {
this.receiveAddress = address; this.receiveAddress = address;
} }
if (this.provider && this.provider.sync) {
assert(!this.provider.update);
this.provider.sync(this, address);
}
return address; return address;
}; };
@ -517,10 +583,6 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
if (this.witness) if (this.witness)
this.addressMap[address.getProgramHash('hex')] = data.path; 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.emit('add address', address);
this.cache.set(data.path, address); this.cache.set(data.path, address);
@ -834,10 +896,6 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) {
if (!tx.checkInputs(height)) if (!tx.checkInputs(height))
return callback(new Error('CheckInputs failed.')); 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); return callback(null, tx);
}); });
}; };
@ -1180,27 +1238,6 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
return total; 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 * Build input scripts and sign inputs for a transaction. Only attempts
* to build/sign inputs that are redeemable by this wallet. * 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. * @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 addresses = this.deriveInputs(tx, index);
var key = this.master.decrypt(passphrase);
var total = 0; var total = 0;
var i; var i, address, key;
for (i = 0; i < addresses.length; i++) for (i = 0; i < addresses.length; i++) {
total += addresses[i].sign(tx, index, type); 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; return total;
}; };
@ -1325,16 +1373,6 @@ Wallet.prototype.getTimeRange = function getTimeRange(options, callback) {
return this.provider.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. * Get public key for current receiving address.
* @param {String?} enc - `"hex"` or `null`. * @param {String?} enc - `"hex"` or `null`.
@ -1575,7 +1613,7 @@ Wallet.prototype.toJSON = function toJSON() {
accountIndex: this.accountIndex, accountIndex: this.accountIndex,
receiveDepth: this.receiveDepth, receiveDepth: this.receiveDepth,
changeDepth: this.changeDepth, changeDepth: this.changeDepth,
master: this.master.toJSON(this.options.passphrase), master: this.master.toJSON(),
accountKey: this.accountKey.xpubkey, accountKey: this.accountKey.xpubkey,
addressMap: this.addressMap, addressMap: this.addressMap,
keys: this.keys.map(function(key) { keys: this.keys.map(function(key) {
@ -1595,7 +1633,7 @@ Wallet.prototype.toJSON = function toJSON() {
* @throws Error on bad decrypt * @throws Error on bad decrypt
*/ */
Wallet.parseJSON = function parseJSON(json, passphrase) { Wallet.parseJSON = function parseJSON(json) {
assert.equal(json.v, 3); assert.equal(json.v, 3);
assert.equal(json.name, 'wallet'); assert.equal(json.name, 'wallet');
@ -1611,10 +1649,12 @@ Wallet.parseJSON = function parseJSON(json, passphrase) {
accountIndex: json.accountIndex, accountIndex: json.accountIndex,
receiveDepth: json.receiveDepth, receiveDepth: json.receiveDepth,
changeDepth: json.changeDepth, changeDepth: json.changeDepth,
master: bcoin.hd.fromJSON(json.master, passphrase), master: MasterKey.fromJSON(json.master),
accountKey: bcoin.hd.fromBase58(json.accountKey),
addressMap: json.addressMap, addressMap: json.addressMap,
keys: json.keys, keys: json.keys.map(function(key) {
passphrase: passphrase return bcoin.hd.fromBase58(key);
})
}; };
}; };
@ -1622,160 +1662,11 @@ Wallet.parseJSON = function parseJSON(json, passphrase) {
* Instantiate a Wallet from a * Instantiate a Wallet from a
* jsonified wallet object. * jsonified wallet object.
* @param {Object} json - The jsonified wallet object. * @param {Object} json - The jsonified wallet object.
* @param {String?} passphrase
* @returns {Wallet} * @returns {Wallet}
*/ */
Wallet.fromJSON = function fromJSON(json, passphrase) { Wallet.fromJSON = function fromJSON(json) {
return new Wallet(Wallet.parseJSON(json, passphrase)); return new Wallet(Wallet.parseJSON(json));
};
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);
}; };
/** /**

View File

@ -232,17 +232,16 @@ WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback)
callback = utils.ensure(callback); callback = utils.ensure(callback);
this.getJSON(id, function(err, json) { this.get(id, function(err, wallet) {
if (err) if (err)
return callback(err); return callback(err);
// Allocate new addresses if necessary. if (!wallet)
json = bcoin.wallet.syncOutputDepth(json, tx); return callback(new Error('No wallet.'));
if (!json) wallet.syncOutputDepth(tx);
return callback();
self.saveJSON(id, json, function(err) { self.save(wallet, function(err) {
if (err) if (err)
return callback(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. * Add a public account/purpose key to the wallet for multisig.
* @param {WalletID} id * @param {WalletID} id
@ -302,17 +265,20 @@ WalletDB.prototype.addKey = function addKey(id, key, callback) {
callback = utils.ensure(callback); callback = utils.ensure(callback);
this.getJSON(id, function(err, json) { this.get(id, function(err, json) {
if (err) if (err)
return callback(err); return callback(err);
if (!wallet)
return callback(new Error('No wallet.'));
try { try {
json = bcoin.wallet.addKey(json, key); wallet.addKey(key);
} catch (e) { } catch (e) {
return callback(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); callback = utils.ensure(callback);
this.getJSON(id, function(err, json) { this.get(id, function(err, wallet) {
if (err) if (err)
return callback(err); return callback(err);
if (!wallet)
return callback(new Error('No wallet.'));
try { try {
json = bcoin.wallet.removeKey(json, key); wallet.removeKey(key);
} catch (e) { } catch (e) {
return callback(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 * Save a "naked" (non-instantiated) wallet. Will
* also index the address table. * also index the address table.
@ -368,33 +322,27 @@ WalletDB.prototype.getJSON = function getJSON(id, callback) {
WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) { WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
var self = this; var self = this;
var data = new Buffer(JSON.stringify(json), 'utf8');
var batch;
callback = utils.ensure(callback); this.db.put('w/' + id, data, function(err) {
return this._saveDB(id, json, function(err, json) {
var batch;
if (err) if (err)
return callback(err); return callback(err);
if (json) { batch = self.db.batch();
batch = self.db.batch();
Object.keys(json.addressMap).forEach(function(address) { Object.keys(json.addressMap).forEach(function(address) {
if (self.tx.filter) if (self.tx.filter)
self.tx.filter.add(address, 'hex'); 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) { return batch.write(function(err) {
if (err) if (err)
return callback(err); return callback(err);
return callback(null, json); return callback(null, json);
}); });
}
return callback(null, json);
}); });
}; };
@ -406,31 +354,31 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
WalletDB.prototype.removeJSON = function removeJSON(id, callback) { WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
var self = this; var self = this;
var batch;
callback = utils.ensure(callback); callback = utils.ensure(callback);
if (typeof id === 'object') this.getJSON(id, function(err, json) {
id = id.id;
return this._removeDB(id, function(err, json) {
var batch;
if (err) if (err)
return callback(err); return callback(err);
if (json) { batch = self.db.batch();
batch = self.db.batch();
Object.keys(json.addressMap).forEach(function(address) { Object.keys(json.addressMap).forEach(function(address) {
batch.del('W/' + address + '/' + json.id); batch.del('W/' + address + '/' + json.id);
}); });
return batch.write(function(err) {
if (err) 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(err);
return callback(null, json); 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)]. * @param {Function} callback - Returns [Error, Object(nakedWallet)].
*/ */
WalletDB.prototype._getDB = function _getDB(id, callback) { WalletDB.prototype.getJSON = function getJSON(id, callback) {
var key = 'w/' + id;
callback = utils.ensure(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') if (err && err.type === 'NotFoundError')
return callback(); 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. * Get a wallet from the database, instantiate, decrypt, and setup provider.
* @param {WalletID} id * @param {WalletID} id
* @param {String?} passphrase
* @param {Function} callback - Returns [Error, {@link Wallet}]. * @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; var self = this;
if (typeof passphrase === 'function') {
callback = passphrase;
passphrase = null;
}
callback = utils.ensure(callback); callback = utils.ensure(callback);
return this.getJSON(id, function(err, options) { return this.getJSON(id, function(err, options) {
@ -540,7 +430,7 @@ WalletDB.prototype.get = function get(id, passphrase, callback) {
return callback(); return callback();
try { try {
options = bcoin.wallet.parseJSON(options, passphrase); options = bcoin.wallet.parseJSON(options);
options.provider = new Provider(self); options.provider = new Provider(self);
wallet = new bcoin.wallet(options); wallet = new bcoin.wallet(options);
} catch (e) { } catch (e) {
@ -558,24 +448,8 @@ WalletDB.prototype.get = function get(id, passphrase, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
WalletDB.prototype.save = function save(id, options, callback) { WalletDB.prototype.save = function save(wallet, callback) {
if (id && typeof id === 'object') { this.saveJSON(wallet.id, wallet.toJSON(), callback);
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);
}; };
/** /**
@ -585,14 +459,6 @@ WalletDB.prototype.save = function save(id, options, callback) {
*/ */
WalletDB.prototype.remove = function remove(id, 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); return this.removeJSON(id, callback);
}; };
@ -603,30 +469,21 @@ WalletDB.prototype.remove = function remove(id, callback) {
* @param {Function} callback - Returns [Error, {@link Wallet}]. * @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; 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) { function create(err, json) {
var wallet; var wallet;
if (err) if (err)
return callback(err); return callback(err);
if (json) if (json) {
return callback(new Error('`' + id + '` already exists.'), null, json); return callback(
new Error('`' + options.id + '` already exists.'),
null,
json);
}
if (self.network.witness) if (self.network.witness)
options.witness = options.witness !== false; options.witness = options.witness !== false;
@ -635,7 +492,7 @@ WalletDB.prototype.create = function create(id, options, callback) {
options.network = self.network; options.network = self.network;
wallet = new bcoin.wallet(options); wallet = new bcoin.wallet(options);
self.saveJSON(wallet.id, wallet.toJSON(), function(err) { self.save(wallet, function(err) {
if (err) if (err)
return callback(err); return callback(err);
@ -643,10 +500,10 @@ WalletDB.prototype.create = function create(id, options, callback) {
}); });
} }
if (!id) if (!options.id)
return create(); 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 * @param {Function} callback
*/ */
WalletDB.prototype.ensure = function ensure(id, options, callback) { WalletDB.prototype.ensure = function ensure(options, callback) {
var self = this; var self = this;
return this.create(options, function(err, wallet, json) {
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) {
if (err && !json) if (err && !json)
return callback(err); return callback(err);
@ -682,7 +525,7 @@ WalletDB.prototype.ensure = function ensure(id, options, callback) {
assert(json); assert(json);
try { try {
options = bcoin.wallet.parseJSON(json, options.passphrase); options = bcoin.wallet.parseJSON(json);
options.provider = new Provider(self); options.provider = new Provider(self);
wallet = new bcoin.wallet(options); wallet = new bcoin.wallet(options);
} catch (e) { } 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}. * @see {@link TXDB#add}.
*/ */
@ -776,7 +565,6 @@ WalletDB.prototype.getCoin = function getCoin(hash, index, callback) {
*/ */
WalletDB.prototype.getHistory = function getHistory(id, callback) { WalletDB.prototype.getHistory = function getHistory(id, callback) {
id = id.id || id;
return this.tx.getHistoryByAddress(id, callback); return this.tx.getHistoryByAddress(id, callback);
}; };
@ -785,7 +573,6 @@ WalletDB.prototype.getHistory = function getHistory(id, callback) {
*/ */
WalletDB.prototype.getCoins = function getCoins(id, callback) { WalletDB.prototype.getCoins = function getCoins(id, callback) {
id = id.id || id;
return this.tx.getCoinsByAddress(id, callback); return this.tx.getCoinsByAddress(id, callback);
}; };
@ -794,7 +581,6 @@ WalletDB.prototype.getCoins = function getCoins(id, callback) {
*/ */
WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) { WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) {
id = id.id || id;
return this.tx.getUnconfirmedByAddress(id, callback); return this.tx.getUnconfirmedByAddress(id, callback);
}; };
@ -803,7 +589,6 @@ WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) {
*/ */
WalletDB.prototype.getBalance = function getBalance(id, callback) { WalletDB.prototype.getBalance = function getBalance(id, callback) {
id = id.id || id;
return this.tx.getBalanceByAddress(id, callback); return this.tx.getBalanceByAddress(id, callback);
}; };
@ -812,7 +597,6 @@ WalletDB.prototype.getBalance = function getBalance(id, callback) {
*/ */
WalletDB.prototype.getLastTime = function getLastTime(id, callback) { WalletDB.prototype.getLastTime = function getLastTime(id, callback) {
id = id.id || id;
return this.tx.getLastTime(id, callback); 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) { WalletDB.prototype.getLast = function getLast(id, limit, callback) {
id = id.id || id;
return this.tx.getLast(id, limit, callback); 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) { WalletDB.prototype.getRange = function getRange(id, options, callback) {
id = id.id || id;
return this.tx.getRange(id, options, callback); 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) { WalletDB.prototype.zapWallet = function zapWallet(id, now, age, callback) {
id = id.id || id;
return this.tx.zap(id, now, age, callback); return this.tx.zap(id, now, age, callback);
}; };
@ -1174,8 +955,8 @@ Provider.prototype.addTX = function addTX(tx, callback) {
* @param {Address} address * @param {Address} address
*/ */
Provider.prototype.update = function update(wallet, address) { Provider.prototype.save = function save(wallet, callback) {
return this.db.update(wallet, address); return this.db.save(wallet, callback);
}; };
/** /**