more wallet work.
This commit is contained in:
parent
3480b8c679
commit
7bb67aa449
@ -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
|
||||
*/
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user