more wallet work.

This commit is contained in:
Christopher Jeffrey 2016-05-27 16:20:11 -07:00
parent 3480b8c679
commit 7bb67aa449
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 170 additions and 142 deletions

View File

@ -14,8 +14,6 @@ var assert = utils.assert;
* @exports KeyRing
* @constructor
* @param {Object} options
* @param {String?} options.label
* @param {Boolean?} options.derived
* @param {HDPrivateKey|HDPublicKey} options.key
* @param {String?} options.path
* @param {Boolean?} options.change
@ -40,10 +38,9 @@ function KeyRing(options) {
options = {};
this.options = options;
this.label = options.label || '';
this.derived = !!options.derived;
this.addressMap = null;
this.network = bcoin.network.get(options.network);
this.key = options.key;
this.path = options.path;
this.change = !!options.change;
@ -63,7 +60,7 @@ function KeyRing(options) {
if (this.m < 1 || this.m > this.n)
throw new Error('m ranges between 1 and n');
this.addKey(this.getPublicKey());
this.addKey(this.key);
if (options.keys) {
for (i = 0; i < options.keys.length; i++)
@ -129,7 +126,13 @@ KeyRing.prototype.removeKey = function removeKey(key) {
*/
KeyRing.prototype.getPublicKey = function getPublicKey(enc) {
return this.key.getPublicKey(enc);
if (enc === 'base58')
return utils.toBase58(this.key);
if (enc === 'hex')
return this.key.toString('hex');
return this.key;
};
/**
@ -455,6 +458,7 @@ KeyRing.prototype.scriptInputs = function scriptInputs(tx, index) {
* Build input scripts and sign inputs for a transaction. Only attempts
* to build/sign inputs that are redeemable by this address.
* @param {MTX} tx
* @param {HDPrivateKey|KeyPair|Buffer} key - Private key.
* @param {Number?} index - Index of input. If not present,
* it will attempt to build and sign all redeemable inputs.
* @param {SighashType?} type
@ -539,6 +543,54 @@ 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() {
return {
v: 1,
name: 'address',
address: this.getAddress(),
network: this.network.type,
change: this.change,
index: this.index,
path: this.path,
key: utils.toBase58(this.key),
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) {
assert.equal(json.v, 1);
assert.equal(json.name, 'address');
return new KeyRing({
nework: json.network,
change: json.change,
index: json.index,
path: json.path,
key: utils.fromBase58(json.key),
type: json.type,
witness: json.witness,
keys: json.keys.map(utils.fromBase58),
m: json.m,
n: json.n
});
};
/*
* Expose
*/

View File

@ -353,8 +353,9 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type,
/**
* Sign an input.
* @param {Number} index - Index of input being signed.
* @param {Address} addr - Address used to sign. The address
* @param {KeyRing} addr - Address used to sign. The address
* must be able to redeem the coin.
* @param {HDPrivateKey|KeyPair|Buffer} key - Private key.
* @param {SighashType} type
* @returns {Boolean} Whether the input was able to be signed.
* @throws on unavailable coins.
@ -623,8 +624,9 @@ MTX.prototype.isSigned = function isSigned() {
/**
* Built input scripts (or witnesses) and sign the inputs.
* @param {Number} index - Index of input being signed.
* @param {Address} addr - Address used to sign. The address
* @param {KeyRing} addr - Address used to sign. The address
* must be able to redeem the coin.
* @param {HDPrivateKey|KeyPair|Buffer} key - Private key.
* @param {SighashType} type
* @returns {Boolean} Whether the input was able to be signed.
* @throws on unavailable coins.
@ -656,7 +658,7 @@ MTX.prototype.sign = function sign(index, addr, key, type) {
* tx.addOutput({ address: ..., value: new bn(100000) });
* tx.addOutput({ address: ..., value: utils.satoshi('0.1') });
* tx.addOutput(receivingWallet, utils.satoshi('0.1'));
* @param {Wallet|Address|Object} obj - Wallet, Address,
* @param {Wallet|KeyRing|Object} obj - Wallet, Address,
* or options (see {@link Script.createOutputScript} for options).
* @param {Amount?} value - Only needs to be present for non-options.
*/

View File

@ -83,6 +83,7 @@ function Wallet(options) {
this.copayBIP45 = options.copayBIP45 || false;
this.lookahead = options.lookahead != null ? options.lookahead : 5;
this.cosignerIndex = -1;
this.initialized = false;
this.type = options.type || 'pubkeyhash';
this.derivation = options.derivation || 'bip44';
@ -154,8 +155,9 @@ Wallet.prototype._init = function _init() {
var self = this;
var i;
assert(!this._initialized);
this._initialized = true;
assert(!this.initialized);
this.initialized = true;
if (Object.keys(this.addressMap).length === 0) {
for (i = 0; i < this.receiveDepth - 1; i++)
@ -178,15 +180,32 @@ Wallet.prototype._init = function _init() {
assert(!this.receiveAddress.change);
assert(this.changeAddress.change);
if (!this.provider)
return;
this.provider.setID(this.id);
this.on('error', function(err) {
bcoin.debug('Wallet Error: %s', err.message);
});
this.setProvider(this.provider, function(err) {
if (err)
return self.emit('error', err);
self.loaded = true;
self.emit('open');
});
};
Wallet.prototype.setProvider = function setProvider(provider, callback) {
var self = this;
if (!provider)
return callback();
if (this.provider !== provider)
this.provider.destroy();
this.provider = provider;
this.provider.setID(this.id);
this.provider.on('error', function(err) {
self.emit('error', err);
});
@ -212,13 +231,7 @@ Wallet.prototype._init = function _init() {
self.emit('unconfirmed', tx);
});
this.provider.open(function(err) {
if (err)
return self.emit('error', err);
self.loaded = true;
self.emit('open');
});
this.provider.open(callback);
};
/**
@ -392,9 +405,6 @@ Wallet.prototype.getID = function getID() {
var publicKey = this.accountKey.publicKey;
var p;
if (this.options.id)
return this.options.id;
p = new BufferWriter();
p.writeU8(0x03);
p.writeU8(0xbe);
@ -484,9 +494,9 @@ Wallet.prototype.deriveChange = function deriveChange(index) {
*/
Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
var path, data, key, options, address;
var i, path, data, key, options, address;
assert(this._initialized);
assert(this.initialized);
if (typeof change === 'string')
path = change;
@ -517,7 +527,7 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
options = {
network: this.network,
key: key,
key: key.publicKey,
change: data.change,
index: data.index,
path: data.path,
@ -525,15 +535,15 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
witness: this.witness,
m: this.m,
n: this.n,
keys: [],
derived: true
keys: []
};
this.keys.forEach(function(key, cosignerIndex) {
var path = this.createPath(cosignerIndex, data.change, data.index);
for (i = 0; i < this.keys.length; i++) {
key = this.keys[i];
path = this.createPath(i, data.change, data.index);
key = key.derive(path);
options.keys.push(key.publicKey);
}, this);
}
address = new bcoin.keyring(options);
@ -724,7 +734,7 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
if (!options)
options = {};
assert(this._initialized);
assert(this.initialized);
this.getCoins(function(err, coins) {
if (err)
@ -858,6 +868,9 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) {
if (!tx.checkInputs(height))
return callback(new Error('CheckInputs failed.'));
if (!self.scriptInputs(tx))
return callback(new Error('scriptInputs failed.'));
return callback(null, tx);
});
};
@ -1095,7 +1108,7 @@ Wallet.prototype._scan = function _scan(getByAddress, callback) {
var depth = { changeDepth: 0, receiveDepth: 0 };
var all = [];
assert(this._initialized);
assert(this.initialized);
(function chainCheck(change) {
var addressIndex = 0;
@ -1501,13 +1514,13 @@ Wallet.prototype.inspect = function inspect() {
network: this.network.type,
m: this.m,
n: this.n,
keyAddress: this._initialized
keyAddress: this.initialized
? this.keyAddress
: null,
scriptAddress: this._initialized
scriptAddress: this.initialized
? this.scriptAddress
: null,
programAddress: this._initialized
programAddress: this.initialized
? this.programAddress
: null,
witness: this.witness,
@ -1653,7 +1666,6 @@ MasterKey.fromJSON = function fromJSON(json) {
});
};
/*
* Expose
*/

View File

@ -40,6 +40,7 @@ function WalletDB(options) {
EventEmitter.call(this);
this.providers = [];
this.options = options;
this.loaded = false;
this.network = bcoin.network.get(options.network);
@ -136,14 +137,14 @@ WalletDB.prototype._init = function _init() {
this.tx.on('tx', function(tx, map) {
self.emit('tx', tx, map);
map.all.forEach(function(id) {
self.emit(id + ' tx', tx);
self.fire(id, 'tx', tx);
});
});
this.tx.on('confirmed', function(tx, map) {
self.emit('confirmed', tx, map);
map.all.forEach(function(id) {
self.emit(id + ' confirmed', tx);
self.fire(id, 'confirmed', tx);
});
utils.forEachSerial(map.output, function(id, next) {
self.syncOutputDepth(id, tx, next);
@ -156,7 +157,7 @@ WalletDB.prototype._init = function _init() {
this.tx.on('unconfirmed', function(tx, map) {
self.emit('unconfirmed', tx, map);
map.all.forEach(function(id) {
self.emit(id + ' unconfirmed', tx);
self.fire(id, 'unconfirmed', tx);
});
});
@ -165,12 +166,12 @@ WalletDB.prototype._init = function _init() {
self.emit('updated', tx, map);
map.all.forEach(function(id) {
self.emit(id + ' updated', tx);
self.fire(id, 'updated', tx);
});
utils.forEachSerial(map.output, function(id, next) {
if (self.listeners('balance').length === 0
&& self.listeners(id + ' balance').length === 0) {
&& !self.hasListener(id, ' balance')) {
return next();
}
@ -181,7 +182,7 @@ WalletDB.prototype._init = function _init() {
balances[id] = balance;
self.emit('balance', balance, id);
self.emit(id + ' balance', balance);
self.fire(id, 'balance', balance);
next();
});
@ -447,28 +448,38 @@ WalletDB.prototype.get = function get(id, callback) {
/**
* Save a wallet to the database (setup ida and encrypt).
* @param {WalletID?} id
* @param {Wallet} options
* @param {Wallet} wallet
* @param {Function} callback
*/
WalletDB.prototype.save = function save(wallet, callback) {
var self = this;
if (Array.isArray(wallet)) {
return utils.forEachSerial(wallet, function(wallet, next) {
self.save(wallet, next);
}, callback);
}
this.saveJSON(wallet.id, wallet.toJSON(), callback);
};
/**
* Remove wallet from the database. Destroy wallet if passed in.
* @param {WalletID|Wallet} id
* @param {WalletID} id
* @param {Function} callback
*/
WalletDB.prototype.remove = function remove(id, callback) {
var self = this;
if (Array.isArray(wallet)) {
return utils.forEachSerial(id, function(id, next) {
self.remove(id, next);
}, callback);
}
return this.removeJSON(id, callback);
};
/**
* Create a new wallet, save to database, setup provider.
* @param {WalletID?} id
* @param {Object} options - See {@link Wallet}.
* @param {Function} callback - Returns [Error, {@link Wallet}].
*/
@ -703,38 +714,53 @@ WalletDB.prototype.provider = function provider() {
};
WalletDB.prototype.register = function register(id, provider) {
if (!this.listeners[id])
this.listeners[id] = [];
if (!this.providers[id])
this.providers[id] = [];
if (this.listeners[id].indexOf(provider) !== -1)
this.listeners[id].push(provider);
if (this.providers[id].indexOf(provider) === -1)
this.providers[id].push(provider);
};
WalletDB.prototype.unregister = function unregister(id, provider) {
var listeners = this.listeners[id];
var providers = this.providers[id];
var i;
if (!listeners)
if (!providers)
return;
i = listeners.indexOf(provider);
i = providers.indexOf(provider);
if (i !== -1)
listeners.splice(i, 1);
providers.splice(i, 1);
if (listeners.length === 0)
delete this.listeners[id];
if (providers.length === 0)
delete this.providers[id];
};
WalletDB.prototype.fire = function fire(id) {
var args = Array.prototype.slice.call(arguments, 1);
var listeners = this.listeners[id];
var providers = this.providers[id];
var i;
if (!listeners)
if (!providers)
return;
for (i = 0; i < listeners.length; i++)
listeners.emit.apply(listener, args);
for (i = 0; i < providers.length; i++)
providers[i].emit.apply(providers[i], args);
};
WalletDB.prototype.hasListener = function hasListener(id, event) {
var providers = this.providers[id];
var i;
if (!providers)
return false;
for (i = 0; i < providers.length; i++) {
if (providers[i].listeners(event).length !== 0)
return true;
}
return false;
};
/**
@ -800,26 +826,7 @@ Provider.prototype.setID = function setID(id) {
assert(!this.id, 'ID has already been set.');
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);
});
this.db.register(this.id, this);
};
/**
@ -832,35 +839,12 @@ Provider.prototype.close =
Provider.prototype.destroy = function destroy(callback) {
callback = utils.ensure(callback);
if (!this.db)
if (!this.id)
return utils.nextTick(callback);
if (this._onTX) {
this.db.removeListener(this.id + ' tx', this._onTX);
delete this._onTX;
}
if (this._onUpdated) {
this.db.removeListener(this.id + ' updated', this._onUpdated);
delete this._onUpdated;
}
if (this._onConfirmed) {
this.db.removeListener(this.id + ' confirmed', this._onConfirmed);
delete this._onConfirmed;
}
if (this._onUnconfirmed) {
this.db.removeListener(this.id + ' unconfirmed', this._onUnconfirmed);
delete this._onUnconfirmed;
}
if (this._onBalance) {
this.db.removeListener(this.id + ' balance', this._onBalance);
delete this._onBalance;
}
this.db.unregister(this.id, this);
this.db = null;
this.id = null;
return utils.nextTick(callback);
};
@ -999,11 +983,9 @@ Provider.prototype.save = function save(wallet, callback) {
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
* Add a key to the wallet.
* @param {HDPublicKey} key
* @param {Function} callback
*/
Provider.prototype.addKey = function addKey(key, callback) {
@ -1011,23 +993,9 @@ Provider.prototype.addKey = function addKey(key, callback) {
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
*/
// Provider.prototype.deriveInputs = function deriveInputs(tx, index, callback) {
// return this.db.deriveInputs(this.id, tx, index, callback);
// };
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
* Remove a key from the wallet.
* @param {HDPublicKey} key
* @param {Function} callback
*/
Provider.prototype.removeKey = function removeKey(key, callback) {
@ -1035,11 +1003,8 @@ Provider.prototype.removeKey = function removeKey(key, callback) {
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
* Create a receiving address.
* @param {Function} callback
*/
Provider.prototype.createReceive = function createReceive(callback) {
@ -1047,11 +1012,8 @@ Provider.prototype.createReceive = function createReceive(callback) {
};
/**
* Notify the provider backend that a new address was
* derived (not technically necessary if you're
* implementing a provider).
* @param {Wallet} wallet
* @param {Address} address
* Create a change address.
* @param {Function} callback
*/
Provider.prototype.createChange = function createChange(callback) {