mtx/wallet: refactor keyring.

This commit is contained in:
Christopher Jeffrey 2016-08-19 00:39:56 -07:00
parent 0f23304a68
commit c836786b99
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
8 changed files with 572 additions and 531 deletions

View File

@ -278,9 +278,6 @@ ec.rand = function rand(min, max) {
ec.verify = function verify(msg, sig, key, historical, high) {
var result;
if (key.getPublicKey)
key = key.getPublicKey();
assert(Buffer.isBuffer(msg));
assert(Buffer.isBuffer(sig));
assert(Buffer.isBuffer(key));
@ -375,8 +372,8 @@ ec.privateKeyVerify = function privateKeyVerify(key) {
ec.sign = function sign(msg, key) {
var sig;
if (key.getPrivateKey)
key = key.getPrivateKey();
assert(Buffer.isBuffer(msg));
assert(Buffer.isBuffer(key));
if (secp256k1) {
// Sign message

View File

@ -2028,7 +2028,7 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) {
RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, args, callback) {
var keys = [];
var keyMap = {};
var k, i, secret, key, addr;
var k, i, secret, key;
var coins, prevout, prev;
var hash, index, script, value;
var redeem, op, j;
@ -2038,12 +2038,12 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg
k = args[2];
for (i = 0; i < k.length; i++) {
secret = k[i];
if (!utils.isBase58(secret))
return callback(new RPCError('Invalid parameter'));
key = bcoin.keypair.fromSecret(secret);
addr = new bcoin.keyring({ publicKey: key.getPublicKey() });
key = { addr: addr, key: key.getPrivateKey() };
keyMap[addr.getPublicKey('hex')] = key;
key = bcoin.keyring.fromSecret(secret);
keyMap[key.getPublicKey('hex')] = key;
keys.push(key);
}
}
@ -2051,14 +2051,18 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg
coins = [];
if (args.length > 1 && Array.isArray(args[1])) {
prevout = args[1];
for (i = 0; i < prevout.length; i++) {
prev = prevout[i];
if (!prev)
return callback(new RPCError('Invalid parameter'));
hash = toHash(prev.txid);
index = prev.vout;
script = prev.scriptPubKey;
value = toSatoshi(prev.amount);
if (!hash
|| !utils.isNumber(index)
|| index < 0
@ -2081,22 +2085,22 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg
if (script.isScripthash() || script.isWitnessScripthash()) {
redeem = bcoin.script.fromRaw(prev.redeemScript, 'hex');
if (!redeem.isMultisig())
continue;
for (j = 1; j < redeem.length - 2; j++) {
for (j = 0; j < redeem.length; j++) {
op = redeem.get(j);
if (!Buffer.isBuffer(op))
continue;
key = keyMap[op.toString('hex')];
if (key) {
key.addr.type = bcoin.keyring.types.MULTISIG;
key.addr.m = redeem.getSmall(0);
key.addr.n = redeem.getSmall(redeem.length - 1);
key.addr.keys = redeem.slice(1, -2);
key.addr.witness = script.isWitnessScripthash();
key.script = redeem;
key.witness = script.isWitnessScripthash();
break;
}
}
}
}
tx.fillCoins(coins);
}
@ -2117,17 +2121,14 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg
for (i = 0; i < keys.length; i++) {
key = keys[i];
key.addr.sign(merged, key.key, null, type);
merged.sign(key, type);
}
this.wallet.sign(merged, { type: type }, function(err) {
if (err)
return callback(err);
for (i = 1; i < txs.length; i++) {
tx = txs[i];
mergeSigs(merged, tx);
}
// TODO: Merge with other txs here.
callback(null, {
hex: merged.toRaw().toString('hex'),
@ -2136,28 +2137,6 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg
});
};
function mergeSigs(a, b) {
var map = {};
var i, input, prev, key, ia, ib;
for (i = 0; i < b.inputs.length; i++) {
input = b.inputs[i];
prev = input.prevout;
key = prev.hash + prev.index;
map[key] = input;
}
for (i = 0; i < b.inputs.length; i++) {
ia = a.inputs[i];
if (!ia || ia.length !== 0)
break;
key = prev.hash + prev.index;
ib = map[key];
if (ib)
ia.script = ib.script;
}
}
RPC.prototype.fundrawtransaction = function fundrawtransaction(args, callback) {
var tx, options, changeAddress, feeRate;
@ -2223,6 +2202,7 @@ RPC.prototype._createRedeem = function _createRedeem(args, callback) {
}
hash = bcoin.address.getHash(key, 'hex');
if (!hash)
return next(new RPCError('Invalid key.'));
@ -2233,7 +2213,7 @@ RPC.prototype._createRedeem = function _createRedeem(args, callback) {
if (!ring)
return next(new RPCError('Invalid key.'));
keys[i] = ring.getPublicKey();
keys[i] = ring.publicKey;
next();
});
@ -2597,9 +2577,7 @@ RPC.prototype.dumpprivkey = function dumpprivkey(args, callback) {
if (!key)
return callback(new RPCError('Wallet is locked.'));
key = ring.derive(key);
callback(null, key.toSecret());
callback(null, ring.toSecret());
});
};
@ -2642,14 +2620,13 @@ RPC.prototype.dumpwallet = function dumpwallet(args, callback) {
if (!key)
return callback(new RPCError('Wallet is locked.'));
key = ring.derive(key);
address = ring.getAddress('base58');
fmt = '%s %s label= addr=%s';
if (ring.change)
fmt = '%s %s change=1 addr=%s';
str = utils.fmt(fmt, key.toSecret(), time, address);
str = utils.fmt(fmt, ring.toSecret(), time, address);
out.push(str);
@ -3826,12 +3803,10 @@ RPC.prototype.signmessage = function signmessage(args, callback) {
if (!key)
return callback(new RPCError('Wallet is locked.'));
key = ring.derive(key);
msg = new Buffer(RPC.magic + msg, 'utf8');
msg = utils.hash256(msg);
sig = bcoin.ec.sign(msg, key);
sig = ring.sign(msg);
callback(null, sig.toString('base64'));
});

View File

@ -16,168 +16,82 @@ var BufferWriter = require('./writer');
var scriptTypes = constants.scriptTypes;
/**
* Represents a key ring which amounts to an address. Used for {@link Wallet}.
* Represents a key ring which amounts to an address.
* @exports KeyRing
* @constructor
* @param {Object} options
* @param {HDPrivateKey|HDPublicKey|KeyPair|Buffer} options.key
* @param {String?} options.name
* @param {Number?} options.account
* @param {Number?} options.change
* @param {Number?} options.index
* @param {String?} options.type - `"pubkeyhash"` or `"multisig"`.
* @param {HDPrivateKey|HDPublicKey|Buffer} options.key
* @param {Buffer[]} options.keys - Shared multisig keys.
* @param {Number?} options.m - Multisig `m` value.
* @param {Number?} options.n - Multisig `n` value.
* @param {Boolean?} options.witness - Whether witness programs are enabled.
*/
function KeyRing(options) {
function KeyRing(options, network) {
if (!(this instanceof KeyRing))
return new KeyRing(options);
return new KeyRing(options, network);
this.network = bcoin.network.get();
this.type = KeyRing.types.PUBKEYHASH;
this.m = 1;
this.n = 1;
this.witness = false;
this.publicKey = null;
this.privateKey = null;
this.script = null;
this.wid = 0;
this.id = null;
this.name = null;
this.account = 0;
this.change = 0;
this.index = 0;
this.key = null;
this.keys = [];
this._keyHash = null;
this._keyAddress = null;
this._program = null;
this._programHash = null;
this._programAddress = null;
this._script = null;
this._scriptHash160 = null;
this._scriptHash256 = null;
this._scriptAddress = null;
this._addressMap = null;
if (options)
this.fromOptions(options);
this.fromOptions(options, network);
}
/**
* KeyRing types.
* @enum {Number}
* @default
*/
KeyRing.types = {
PUBKEYHASH: 0,
MULTISIG: 1
};
/**
* KeyRing types by value.
* @const {RevMap}
*/
KeyRing.typesByVal = {
0: 'pubkeyhash',
1: 'multisig'
};
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
KeyRing.prototype.fromOptions = function fromOptions(options) {
var i;
KeyRing.prototype.fromOptions = function fromOptions(options, network) {
var key = toKey(options);
assert(options.key);
if (Buffer.isBuffer(key))
return this.fromKey(key, network);
key = toKey(options.key);
if (options.privateKey)
key = toKey(options.privateKey);
if (options.publicKey)
key = toKey(options.publicKey);
if (options.network)
this.network = bcoin.network.get(options.network);
if (options.type != null) {
if (typeof options.type === 'string') {
this.type = KeyRing.types[options.type.toUpperCase()];
assert(this.type != null);
} else {
assert(typeof options.type === 'number');
this.type = options.type;
assert(KeyRing.typesByVal[this.type]);
}
}
if (options.m != null) {
assert(utils.isNumber(options.m));
this.m = options.m;
}
if (options.n != null) {
assert(utils.isNumber(options.n));
this.n = options.n;
}
if (options.witness != null) {
assert(typeof options.witness === 'boolean');
this.witness = options.witness;
}
if (options.wid) {
assert(utils.isNumber(options.wid));
this.wid = options.wid;
}
if (options.keys)
return this.fromKeys(key, options.m, options.n, options.keys, this.network);
if (options.id) {
assert(utils.isName(options.id));
this.id = options.id;
}
if (options.script)
return this.fromScript(key, options.script, this.network);
if (options.name) {
assert(utils.isName(options.name));
this.name = options.name;
}
if (options.account != null) {
assert(utils.isNumber(options.account));
this.account = options.account;
}
if (options.change != null) {
assert(utils.isNumber(options.change));
this.change = options.change;
}
if (options.index != null) {
assert(utils.isNumber(options.index));
this.index = options.index;
}
this.key = options.key;
if (this.key.getPublicKey)
this.key = this.key.getPublicKey();
assert(Buffer.isBuffer(this.key));
if (this.n > 1)
this.type = KeyRing.types.MULTISIG;
if (this.m < 1 || this.m > this.n)
throw new Error('m ranges between 1 and n');
this.addKey(this.key);
if (options.keys) {
assert(Array.isArray(options.keys));
for (i = 0; i < options.keys.length; i++)
this.addKey(options.keys[i]);
}
return this;
this.fromKey(key, this.network);
};
/**
@ -190,6 +104,234 @@ KeyRing.fromOptions = function fromOptions(options) {
return new KeyRing().fromOptions(options);
};
/**
* Inject data from private key.
* @private
* @param {Buffer} privateKey
* @param {Boolean?} compressed
* @param {(NetworkType|Network}) network
*/
KeyRing.prototype.fromPrivate = function fromPrivate(privateKey, network) {
assert(Buffer.isBuffer(privateKey), 'Private key must be a buffer.');
assert(bcoin.ec.privateKeyVerify(privateKey), 'Not a valid private key.');
this.network = bcoin.network.get(network);
this.privateKey = privateKey;
this.publicKey = bcoin.ec.publicKeyCreate(this.privateKey, true);
return this;
};
/**
* Instantiate keyring from a private key.
* @param {Buffer} privateKey
* @param {Boolean?} compressed
* @param {(NetworkType|Network}) network
* @returns {KeyRing}
*/
KeyRing.fromPrivate = function fromPrivate(privateKey, network) {
return new KeyRing().fromPrivate(privateKey, network);
};
/**
* Inject data from public key.
* @private
* @param {Buffer} privateKey
* @param {(NetworkType|Network}) network
*/
KeyRing.prototype.fromPublic = function fromPublic(publicKey, network) {
assert(Buffer.isBuffer(publicKey), 'Public key must be a buffer.');
assert(bcoin.ec.publicKeyVerify(publicKey), 'Not a valid public key.');
this.network = bcoin.network.get(network);
this.publicKey = publicKey;
return this;
};
/**
* Generate a keyring.
* @param {(Network|NetworkType)?} network
* @returns {KeyRing}
*/
KeyRing.generate = function(witness, network) {
var key = new KeyRing();
key.network = bcoin.network.get(network);
key.privateKey = bcoin.ec.generatePrivateKey();
key.publicKey = bcoin.ec.publicKeyCreate(key.privateKey, true);
key.witness = !!witness;
return key;
};
/**
* Instantiate keyring from a public key.
* @param {Buffer} publicKey
* @param {(NetworkType|Network}) network
* @returns {KeyRing}
*/
KeyRing.fromPublic = function fromPublic(publicKey, network) {
return new KeyRing().fromPublic(publicKey, network);
};
/**
* Inject data from public key.
* @private
* @param {Buffer} privateKey
* @param {(NetworkType|Network}) network
*/
KeyRing.prototype.fromKey = function fromKey(key, network) {
assert(Buffer.isBuffer(key), 'Key must be a buffer.');
assert(key.length === 32 || key.length === 33, 'Not a key.');
if (key.length === 33)
return this.fromPublic(key, network);
return this.fromPrivate(key, network);
};
/**
* Instantiate keyring from a public key.
* @param {Buffer} publicKey
* @param {(NetworkType|Network}) network
* @returns {KeyRing}
*/
KeyRing.fromKey = function fromKey(key, network) {
return new KeyRing().fromKey(key, network);
};
/**
* Inject data from public key.
* @private
* @param {Buffer} key
* @param {Number} m
* @param {Number} n
* @param {Buffer[]} keys
* @param {(NetworkType|Network}) network
*/
KeyRing.prototype.fromKeys = function fromKeys(key, m, n, keys, network) {
var script = bcoin.script.fromMultisig(m, n, keys);
this.fromScript(key, script, network);
return this;
};
/**
* Instantiate keyring from keys.
* @param {Buffer} key
* @param {Number} m
* @param {Number} n
* @param {Buffer[]} keys
* @param {(NetworkType|Network}) network
* @returns {KeyRing}
*/
KeyRing.fromKeys = function fromKeys(key, m, n, keys, network) {
return new KeyRing().fromKeys(key, m, n, keys, network);
};
/**
* Inject data from script.
* @private
* @param {Buffer} key
* @param {Script} script
* @param {(NetworkType|Network}) network
*/
KeyRing.prototype.fromScript = function fromScript(key, script, network) {
assert(script instanceof bcoin.script, 'Non-script passed into KeyRing.');
this.fromKey(key, network);
this.script = script;
return this;
};
/**
* Instantiate keyring from script.
* @param {Buffer} key
* @param {Script} script
* @param {(NetworkType|Network}) network
* @returns {KeyRing}
*/
KeyRing.fromScript = function fromScript(key, script, network) {
return new KeyRing().fromScript(key, script, network);
};
/**
* Convert key to a CBitcoinSecret.
* @param {(Network|NetworkType)?} network
* @returns {Base58String}
*/
KeyRing.prototype.toSecret = function toSecret(network) {
var p = new BufferWriter();
assert(this.privateKey, 'Cannot serialize without private key.');
if (!network)
network = this.network;
network = bcoin.network.get(network);
p.writeU8(network.keyPrefix.privkey);
p.writeBytes(this.privateKey);
p.writeU8(1);
p.writeChecksum();
return utils.toBase58(p.render());
};
/**
* Inject properties from serialized CBitcoinSecret.
* @private
* @param {Base58String} secret
*/
KeyRing.prototype.fromSecret = function fromSecret(data) {
var p = new BufferReader(utils.fromBase58(data), true);
var i, prefix, version, type, key, compressed;
version = p.readU8();
for (i = 0; i < network.types.length; i++) {
type = network.types[i];
prefix = network[type].keyPrefix.privkey;
if (version === prefix)
break;
}
assert(i < network.types.length, 'Network not found.');
key = p.readBytes(32);
if (p.left() > 4) {
assert(p.readU8() === 1, 'Bad compression flag.');
compressed = true;
} else {
compressed = false;
}
p.verifyChecksum();
assert(compressed === false, 'Cannot handle uncompressed.');
return this.fromPrivate(key, type);
};
/**
* Instantiate a keyring from a serialized CBitcoinSecret.
* @param {Base58String} secret
* @returns {KeyRing}
*/
KeyRing.fromSecret = function fromSecret(data) {
return new KeyRing().fromSecret(data);
};
/**
* Inject properties from account object.
* @private
@ -201,25 +343,20 @@ KeyRing.fromOptions = function fromOptions(options) {
*/
KeyRing.prototype.fromAccount = function fromAccount(account, key, keys, change, index) {
var i;
this.network = account.network;
this.key = key.publicKey;
this.publicKey = key.publicKey;
if (account.n > 1)
this.script = bcoin.script.fromMultisig(account.m, account.n, keys);
this.witness = account.witness;
this.wid = account.wid;
this.id = account.id;
this.name = account.name;
this.account = account.accountIndex;
this.change = change;
this.index = index;
this.type = account.type;
this.witness = account.witness;
this.m = account.m;
this.n = account.n;
this.addKey(this.key);
for (i = 0; i < keys.length; i++)
this.addKey(keys[i]);
return this;
};
@ -238,36 +375,6 @@ KeyRing.fromAccount = function fromAccount(account, key, keys, change, index) {
return new KeyRing().fromAccount(account, key, keys, change, index);
};
/**
* Add a key to shared keys.
* @param {Buffer} key
*/
KeyRing.prototype.addKey = function addKey(key) {
assert(Buffer.isBuffer(key));
utils.binaryInsert(this.keys, key, utils.cmp, true);
if (this.keys.length > this.n) {
utils.binaryRemove(this.keys, key, utils.cmp);
throw new Error('Cannot add more keys.');
}
};
/**
* Remove a key from shared keys.
* @param {Buffer} key
*/
KeyRing.prototype.removeKey = function removeKey(key) {
assert(Buffer.isBuffer(key));
if (this.keys.length === this.n)
throw new Error('Cannot remove key.');
utils.binaryRemove(this.keys, key, utils.cmp);
};
/**
* Get public key.
* @param {String?} enc - `"hex"` or `null`.
@ -276,12 +383,12 @@ KeyRing.prototype.removeKey = function removeKey(key) {
KeyRing.prototype.getPublicKey = function getPublicKey(enc) {
if (enc === 'base58')
return utils.toBase58(this.key);
return utils.toBase58(this.publicKey);
if (enc === 'hex')
return this.key.toString('hex');
return this.publicKey.toString('hex');
return this.key;
return this.publicKey;
};
/**
@ -290,23 +397,7 @@ KeyRing.prototype.getPublicKey = function getPublicKey(enc) {
*/
KeyRing.prototype.getScript = function getScript() {
var redeem;
if (this.type !== KeyRing.types.MULTISIG)
return;
if (!this._script) {
assert(this.keys.length === this.n, 'Not all keys have been added.');
redeem = bcoin.script.fromMultisig(this.m, this.n, this.keys);
if (redeem.getSize() > 520)
throw new Error('Redeem script too large (520 byte limit).');
this._script = redeem;
}
return this._script;
return this.script;
};
/**
@ -321,14 +412,12 @@ KeyRing.prototype.getProgram = function getProgram() {
return;
if (!this._program) {
if (this.type === KeyRing.types.PUBKEYHASH) {
hash = utils.hash160(this.getPublicKey());
program = bcoin.script.fromProgram(0, hash);
} else if (this.type === KeyRing.types.MULTISIG) {
hash = utils.sha256(this.getScript().toRaw());
if (!this.script) {
hash = utils.hash160(this.publicKey);
program = bcoin.script.fromProgram(0, hash);
} else {
assert(false, 'Unknown address type.');
hash = this.script.sha256();
program = bcoin.script.fromProgram(0, hash);
}
this._program = program;
}
@ -348,7 +437,7 @@ KeyRing.prototype.getProgramHash = function getProgramHash(enc) {
return;
if (!this._programHash)
this._programHash = utils.hash160(this.getProgram().toRaw());
this._programHash = this.getProgram().hash160();
return enc === 'hex'
? this._programHash.toString('hex')
@ -398,11 +487,11 @@ KeyRing.prototype.getScriptHash = function getScriptHash(enc) {
*/
KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) {
if (this.type !== KeyRing.types.MULTISIG)
if (!this.script)
return;
if (!this._scriptHash160)
this._scriptHash160 = utils.hash160(this.getScript().toRaw());
this._scriptHash160 = this.script.hash160();
return enc === 'hex'
? this._scriptHash160.toString('hex')
@ -416,11 +505,11 @@ KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) {
*/
KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) {
if (this.type !== KeyRing.types.MULTISIG)
if (!this.script)
return;
if (!this._scriptHash256)
this._scriptHash256 = utils.sha256(this.getScript().toRaw());
this._scriptHash256 = this.script.sha256();
return enc === 'hex'
? this._scriptHash256.toString('hex')
@ -436,7 +525,7 @@ KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) {
KeyRing.prototype.getScriptAddress = function getScriptAddress(enc) {
var hash, address;
if (this.type !== KeyRing.types.MULTISIG)
if (!this.script)
return;
if (!this._scriptAddress) {
@ -464,7 +553,7 @@ KeyRing.prototype.getScriptAddress = function getScriptAddress(enc) {
KeyRing.prototype.getKeyHash = function getKeyHash(enc) {
if (!this._keyHash)
this._keyHash = utils.hash160(this.getPublicKey());
this._keyHash = utils.hash160(this.publicKey);
return enc === 'hex'
? this._keyHash.toString('hex')
@ -516,7 +605,7 @@ KeyRing.prototype.compile = function compile(hash, type, version) {
*/
KeyRing.prototype.getHash = function getHash(enc) {
if (this.type === KeyRing.types.MULTISIG)
if (this.script)
return this.getScriptHash(enc);
return this.getKeyHash(enc);
};
@ -528,25 +617,33 @@ KeyRing.prototype.getHash = function getHash(enc) {
*/
KeyRing.prototype.getAddress = function getAddress(enc) {
if (this.type === KeyRing.types.MULTISIG)
if (this.script)
return this.getScriptAddress(enc);
return this.getKeyAddress(enc);
};
/**
* Create the address map for testing txs.
* @returns {AddressMap}
* Test an address hash against hash and program hash.
* @param {Buffer} hash
* @returns {Boolean}
*/
KeyRing.prototype.getAddressMap = function getAddressMap() {
if (!this._addressMap) {
this._addressMap = {};
this._addressMap[this.getHash('hex')] = true;
if (this.witness)
this._addressMap[this.getProgramHash('hex')] = true;
KeyRing.prototype.ownHash = function ownHash(hash) {
if (!hash)
return false;
if (utils.equal(hash, this.keyHash))
return true;
if (utils.equal(hash, this.scriptHash))
return true;
if (this.witness) {
if (utils.equal(hash, this.programHash))
return true;
}
return this._addressMap;
return false;
};
/**
@ -557,8 +654,7 @@ KeyRing.prototype.getAddressMap = function getAddressMap() {
*/
KeyRing.prototype.ownInput = function ownInput(tx, index) {
var addressMap = this.getAddressMap();
var input, hash;
var input;
if (tx instanceof bcoin.input) {
input = tx;
@ -567,12 +663,7 @@ KeyRing.prototype.ownInput = function ownInput(tx, index) {
assert(input, 'Input does not exist.');
}
hash = input.getHash('hex');
if (!hash)
return false;
return addressMap[hash] === true;
return this.ownHash(input.getHash());
};
/**
@ -583,8 +674,7 @@ KeyRing.prototype.ownInput = function ownInput(tx, index) {
*/
KeyRing.prototype.ownOutput = function ownOutput(tx, index) {
var addressMap = this.getAddressMap();
var output, hash;
var output;
if (tx instanceof bcoin.output) {
output = tx;
@ -593,61 +683,53 @@ KeyRing.prototype.ownOutput = function ownOutput(tx, index) {
assert(output, 'Output does not exist.');
}
hash = output.getHash('hex');
if (!hash)
return false;
return addressMap[hash] === true;
return this.ownHash(output.getHash());
};
/**
* Test a hash against script hashes to
* find the correct redeem script, if any.
* @param {Buffer} hash
* @returns {Script|null}
*/
KeyRing.prototype.getRedeem = function(hash) {
if (this.program && utils.equal(hash, this.programHash))
return this.program;
if (this.program) {
if (utils.equal(hash, this.programHash))
return this.program;
}
if (this.script && utils.equal(hash, this.scriptHash160))
return this.script;
if (this.script) {
if (utils.equal(hash, this.scriptHash160))
return this.script;
if (this.script && utils.equal(hash, this.scriptHash256))
return this.script;
if (utils.equal(hash, this.scriptHash256))
return this.script;
}
return null;
};
/**
* Build input scripts templates for a transaction (does not
* sign, only creates signature slots). Only builds scripts
* for 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.
* @returns {Number} Total number of scripts built.
* Sign a message.
* @param {Buffer} msg
* @returns {Buffer} Signature in DER format.
*/
KeyRing.prototype.scriptInputs = function scriptInputs(tx) {
return tx.template(this.publicKey, this.script);
KeyRing.prototype.sign = function sign(msg) {
assert(this.privateKey, 'Cannot sign without private key.');
return bcoin.ec.sign(msg, this.privateKey);
};
/**
* 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.
* @returns {Number} Total number of inputs scripts built and signed.
* Verify a message.
* @param {Buffer} msg
* @param {Buffer} sig - Signature in DER format.
* @returns {Boolean}
*/
KeyRing.prototype.sign = function sign(tx, key) {
return tx.sign(key, this.script);
};
/**
* Derive to address index.
* @param {HDPrivateKey} key
* @returns {HDPrivateKey}
*/
KeyRing.prototype.derive = function derive(key) {
if (key.isMaster())
key = key.deriveAccount44(this.account);
return key.derive(this.change).derive(this.index);
KeyRing.prototype.verify = function verify(msg, sig) {
return bcoin.ec.verify(msg, sig, this.publicKey);
};
/**
@ -655,28 +737,20 @@ KeyRing.prototype.derive = function derive(key) {
* @returns {ScriptType}
*/
KeyRing.prototype.getScriptType = function getScriptType() {
switch (this.type) {
case KeyRing.types.PUBKEYHASH:
return this.witness
? scriptTypes.WITNESSPUBKEYHASH
: scriptTypes.PUBKEYHASH;
case KeyRing.types.MULTISIG:
return this.witness
? scriptTypes.WITNESSSCRIPTHASH
: scriptTypes.SCRIPTHASH;
default:
assert(false, 'Bad keyring type.');
break;
}
KeyRing.prototype.getType = function getType() {
if (this.program)
return this.program.getType();
if (this.script)
return this.script.getType();
return scriptTypes.PUBKEYHASH;
};
KeyRing.prototype.__defineGetter__('publicKey', function() {
return this.getPublicKey();
});
/*
* Getters
*/
KeyRing.prototype.__defineGetter__('script', function() {
return this.getScript();
KeyRing.prototype.__defineGetter__('type', function() {
return this.getType();
});
KeyRing.prototype.__defineGetter__('scriptHash', function() {
@ -723,6 +797,15 @@ KeyRing.prototype.__defineGetter__('address', function() {
return this.getAddress();
});
/**
* Inspect keyring.
* @returns {Object}
*/
KeyRing.prototype.inspect = function inspect() {
return this.toJSON();
};
/**
* Convert an KeyRing to a more json-friendly object.
* @returns {Object}
@ -731,20 +814,16 @@ KeyRing.prototype.__defineGetter__('address', function() {
KeyRing.prototype.toJSON = function toJSON() {
return {
network: this.network.type,
type: KeyRing.typesByVal[this.type].toLowerCase(),
m: this.m,
n: this.n,
witness: this.witness,
key: this.publicKey.toString('hex'),
script: this.script ? this.script.toRaw().toString('hex') : null,
type: constants.scriptTypesByVal[this.type].toLowerCase(),
wid: this.wid,
id: this.id,
name: this.name,
account: this.account,
change: this.change,
index: this.index,
key: this.key.toString('hex'),
keys: this.keys.map(function(key) {
return key.toString('hex');
}),
address: this.getAddress('base58'),
programAddress: this.getProgramAddress('base58')
};
@ -757,39 +836,31 @@ KeyRing.prototype.toJSON = function toJSON() {
*/
KeyRing.prototype.fromJSON = function fromJSON(json) {
var i;
assert(json);
assert(typeof json.network === 'string');
assert(typeof json.type === 'string');
assert(utils.isNumber(json.m));
assert(utils.isNumber(json.n));
assert(typeof json.witness === 'boolean');
assert(typeof json.publicKey === 'string');
assert(!json.script || typeof json.script === 'string');
assert(!json.wid || utils.isNumber(json.wid));
assert(!json.id || utils.isName(json.id));
assert(!json.name || utils.isName(json.name));
assert(utils.isNumber(json.account));
assert(utils.isNumber(json.change));
assert(utils.isNumber(json.index));
assert(typeof json.key === 'string');
assert(Array.isArray(json.keys));
this.nework = bcoin.network.get(json.network);
this.type = KeyRing.types[json.type.toUpperCase()];
this.m = json.m;
this.n = json.n;
this.witness = json.witness;
this.publicKey = new Buffer(json.publicKey, 'hex');
if (json.script)
this.script = new Buffer(json.script, 'hex');
this.wid = json.wid;
this.name = json.name;
this.account = json.account;
this.change = json.change;
this.index = json.index;
this.key = new Buffer(json.key, 'hex');
assert(this.type != null);
for (i = 0; i < json.keys.length; i++)
this.keys.push(new Buffer(json.keys[i], 'hex'));
return this;
};
@ -814,21 +885,18 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
var i;
p.writeU32(this.network.magic);
p.writeU8(this.type);
p.writeU8(this.m);
p.writeU8(this.n);
p.writeU8(this.witness ? 1 : 0);
p.writeU32(this.wid);
p.writeVarString(this.id, 'utf8');
p.writeVarString(this.name, 'utf8');
p.writeU32(this.account);
p.writeU32(this.change);
p.writeU32(this.index);
p.writeVarBytes(this.key);
p.writeU8(this.keys.length);
p.writeVarBytes(this.publicKey);
for (i = 0; i < this.keys.length; i++)
p.writeVarBytes(this.keys[i]);
if (this.privateKey)
p.writeVarBytes(this.privateKey);
else
p.writeVarint(0);
if (this.script)
p.writeVarBytes(this.script.toRaw());
else
p.writeVarint(0);
if (!writer)
p = p.render();
@ -847,24 +915,18 @@ KeyRing.prototype.fromRaw = function fromRaw(data) {
var i, count;
this.network = bcoin.network.fromMagic(p.readU32());
this.type = p.readU8();
this.m = p.readU8();
this.n = p.readU8();
this.witness = p.readU8() === 1;
this.wid = p.readU32();
this.id = p.readVarString('utf8');
this.name = p.readVarString('utf8');
this.account = p.readU32();
this.change = p.readU32();
this.index = p.readU32();
this.key = p.readVarBytes();
this.publicKey = p.readVarBytes();
assert(KeyRing.typesByVal[this.type]);
this.privateKey = p.readVarBytes();
count = p.readU8();
if (this.privateKey.length === 0)
this.privateKey = null;
for (i = 0; i < count; i++)
this.keys.push(p.readVarBytes());
this.script = p.readVarBytes();
if (this.script.length === 0)
this.script = null;
return this;
};
@ -887,10 +949,27 @@ KeyRing.fromRaw = function fromRaw(data) {
KeyRing.isKeyRing = function isKeyRing(obj) {
return obj
&& Array.isArray(obj.keys)
&& typeof obj.getAddressMap === 'function';
&& Buffer.isBuffer(obj.publicKey)
&& typeof obj.toSecret === 'function';
};
/*
* Helpers
*/
function toKey(opt) {
if (!opt)
return opt;
if (opt.getPrivateKey)
return opt.getPrivateKey();
if (opt.getPublicKey)
return opt.getPublicKey();
return opt;
}
/*
* Expose
*/

View File

@ -211,12 +211,11 @@ MTX.prototype.addOutput = function addOutput(options, value) {
* Build input script (or witness) templates (with
* OP_0 in place of signatures).
* @param {Number} index - Input index.
* @param {Buffer} key - Public key.
* @param {Script} script - Redeem script.
* @param {KeyRing} ring
* @returns {Boolean} Whether the script was able to be built.
*/
MTX.prototype.scriptInput = function scriptInput(index, key, script) {
MTX.prototype.scriptInput = function scriptInput(index, ring) {
var input = this.inputs[index];
var prev, redeem;
@ -233,9 +232,6 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
return true;
}
if (key.getPublicKey)
key = key.getPublicKey();
// Get the previous output's script
prev = input.coin.script;
@ -243,7 +239,7 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
// with segwit: figuring out where the redeem script and witness
// redeem scripts go.
if (prev.isScripthash()) {
redeem = this._getRedeem(prev.get(1), key, script);
redeem = ring.getRedeem(prev.get(1));
if (!redeem)
return false;
@ -252,12 +248,12 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
if (redeem.isProgram()) {
// P2WSH nested within pay-to-scripthash.
if (redeem.isWitnessScripthash()) {
prev = this._getRedeem(redeem.get(1), key, script);
prev = ring.getRedeem(redeem.get(1));
if (!prev)
return false;
if (!this.scriptVector(prev, input.witness, key))
if (!this.scriptVector(prev, input.witness, ring))
return false;
input.witness.push(prev.toRaw());
@ -269,9 +265,9 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
// P2WPKH nested within pay-to-scripthash.
if (redeem.isWitnessPubkeyhash()) {
prev = Script.fromPubkeyhash(utils.hash160(key));
prev = Script.fromPubkeyhash(ring.keyHash);
if (!this.scriptVector(prev, input.witness, key))
if (!this.scriptVector(prev, input.witness, ring))
return false;
input.script.push(redeem.toRaw());
@ -285,7 +281,7 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
}
// Regular P2SH.
if (!this.scriptVector(redeem, input.script, key))
if (!this.scriptVector(redeem, input.script, ring))
return false;
input.script.push(redeem.toRaw());
@ -298,12 +294,12 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
if (prev.isProgram()) {
// Bare P2WSH.
if (prev.isWitnessScripthash()) {
redeem = this._getRedeem(prev.get(1), key, script);
redeem = ring.getRedeem(prev.get(1));
if (!redeem)
return false;
if (!this.scriptVector(redeem, input.witness, key))
if (!this.scriptVector(redeem, input.witness, ring))
return false;
input.witness.push(redeem.toRaw());
@ -316,7 +312,7 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
if (prev.isWitnessPubkeyhash()) {
prev = Script.fromPubkeyhash(prev.get(1));
if (!this.scriptVector(prev, input.witness, key))
if (!this.scriptVector(prev, input.witness, ring))
return false;
input.script.compile();
@ -329,7 +325,7 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
}
// Wow, a normal output! Praise be to Jengus and Gord.
return this.scriptVector(prev, input.script, key);
return this.scriptVector(prev, input.script, ring);
};
/**
@ -337,16 +333,16 @@ MTX.prototype.scriptInput = function scriptInput(index, key, script) {
* based on a previous script.
* @param {Script} prev
* @param {Witness|Script} vector
* @param {Buffer} key
* @param {Buffer} ring
* @return {Boolean}
*/
MTX.prototype.scriptVector = function scriptVector(prev, vector, key) {
MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
var i, n;
// P2PK
if (prev.isPubkey()) {
if (!utils.equal(prev.get(1), key))
if (!utils.equal(prev.get(1), ring.publicKey))
return false;
vector.set(0, opcodes.OP_0);
@ -356,18 +352,18 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, key) {
// P2PKH
if (prev.isPubkeyhash()) {
if (!utils.equal(prev.get(2), utils.hash160(key)))
if (!utils.equal(prev.get(2), ring.keyHash))
return false;
vector.set(0, opcodes.OP_0);
vector.set(1, key);
vector.set(1, ring.publicKey);
return true;
}
// Multisig
if (prev.isMultisig()) {
if (prev.indexOf(key) === -1)
if (prev.indexOf(ring.publicKey) === -1)
return false;
// Technically we should create m signature slots,
@ -388,52 +384,6 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, key) {
return false;
};
/**
* Calculate a redeem script based on hash.
* Test against passed in redeem script.
* @private
* @param {Buffer} hash - 32 or 20 byte hash.
* @param {Buffer} key - Public key.
* @param {Script} script - Known redeem script.
* @returns {Script|null}
*/
MTX.prototype._getRedeem = function getRedeem(hash, key, script) {
var program;
if (!key)
return;
switch (hash.length) {
case 20:
program = bcoin.script.fromProgram(0, utils.hash160(key));
if (utils.equal(program.hash160(), hash))
return program;
if (!script)
return;
program = script.forWitness();
if (utils.equal(program.hash160(), hash))
return program;
if (utils.equal(script.hash160(), hash))
return script;
break;
case 32:
if (!script)
return;
if (utils.equal(script.sha256(), hash))
return script;
break;
}
};
/**
* Sign an input.
* @param {Number} index - Index of input being signed.
@ -454,9 +404,6 @@ MTX.prototype.signInput = function signInput(index, key, type) {
if (!input.coin)
return false;
if (key.getPrivateKey)
key = key.getPrivateKey();
// Get the previous output's script
prev = input.coin.script;
vector = input.script;
@ -832,26 +779,23 @@ MTX.prototype.isSigned = function isSigned() {
/**
* Built input scripts (or witnesses) and sign the inputs.
* @param {Number} index - Index of input being signed.
* @param {KeyRing} ring - 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.
*/
MTX.prototype.template = function template(key, script) {
MTX.prototype.template = function template(ring) {
var total = 0;
var i;
if (key.getPublicKey)
key = key.getPublicKey();
for (i = 0; i < this.inputs.length; i++) {
// Build script for input
if (!this.scriptInput(i, key, script))
if (!ring.ownInput(this, i))
continue;
// Build script for input
if (!this.scriptInput(i, ring))
continue;
total++;
}
@ -860,27 +804,32 @@ MTX.prototype.template = function template(key, script) {
/**
* Built input scripts (or witnesses) and sign the inputs.
* @param {Number} index - Index of input being signed.
* @param {KeyRing} ring - 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.
*/
MTX.prototype.sign = function sign(key, script, type) {
MTX.prototype.sign = function sign(ring, type) {
var total = 0;
var i, pub;
var i, key;
if (key.getPrivateKey)
key = key.getPrivateKey();
if (Array.isArray(ring)) {
for (i = 0; i < ring.length; i++)
total += this.sign(ring[i], type);
return total;
}
pub = bcoin.ec.publicKeyCreate(key, true);
key = ring.privateKey;
assert(key, 'No private key available.');
for (i = 0; i < this.inputs.length; i++) {
if (!ring.ownInput(this, i))
continue;
// Build script for input
if (!this.scriptInput(i, pub, script))
if (!this.scriptInput(i, ring))
continue;
// Sign input
@ -893,6 +842,36 @@ MTX.prototype.sign = function sign(key, script, type) {
return total;
};
/**
* Sign the transaction inputs on the worker pool
* (if workers are enabled).
* @param {KeyRing} ring
* @param {SighashType?} type
* @param {Function} callback
* @returns {Boolean} Whether the inputs are valid.
*/
MTX.prototype.signAsync = function signAsync(ring, type, callback) {
var result;
if (typeof type === 'function') {
callback = type;
type = null;
}
if (!bcoin.useWorkers) {
callback = utils.asyncify(callback);
try {
result = this.sign(ring, type);
} catch (e) {
return callback(e);
}
return callback(null, result);
}
bcoin.workerPool.sign(this, ring, type, callback);
};
/**
* Test whether the transaction at least
* has all script templates built.

View File

@ -15,8 +15,6 @@ var assert = utils.assert;
var BufferReader = require('./reader');
var BufferWriter = require('./writer');
var TXDB = require('./txdb');
var keyTypes = bcoin.keyring.types;
var keyTypesByVal = bcoin.keyring.typesByVal;
/**
* BIP44 Wallet
@ -943,12 +941,12 @@ Wallet.prototype.createTX = function createTX(options, callback, force) {
if (!tx.checkInputs(self.db.height))
return callback(new Error('CheckInputs failed.'));
self.scriptInputs(tx, function(err, total) {
self.template(tx, function(err, total) {
if (err)
return callback(err);
if (total === 0)
return callback(new Error('scriptInputs failed.'));
return callback(new Error('template failed.'));
callback(null, tx);
});
@ -1029,11 +1027,16 @@ Wallet.prototype.resend = function resend(callback) {
* @param {Function} callback - Returns [Error, {@link KeyRing}[]].
*/
Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
var self = this;
var rings = [];
var ring;
if (typeof master === 'function') {
callback = master;
master = null;
}
this.getInputPaths(tx, function(err, paths) {
if (err)
return callback(err);
@ -1046,7 +1049,7 @@ Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
if (!account)
return next();
ring = account.deriveAddress(path.change, path.index);
ring = account.deriveAddress(path.change, path.index, master);
rings.push(ring);
next();
@ -1088,7 +1091,7 @@ Wallet.prototype.getKeyring = function getKeyring(address, callback) {
if (!account)
return callback();
ring = account.deriveAddress(path.change, path.index);
ring = account.deriveAddress(path.change, path.index, self.master.key);
callback(null, ring);
});
@ -1365,16 +1368,18 @@ Wallet.prototype.getRedeem = function getRedeem(hash, callback) {
* (total number of scripts built).
*/
Wallet.prototype.scriptInputs = function scriptInputs(tx, callback) {
Wallet.prototype.template = function template(tx, callback) {
var total = 0;
var i;
var i, ring;
this.deriveInputs(tx, function(err, rings) {
if (err)
return callback(err);
for (i = 0; i < rings.length; i++)
total += rings[i].scriptInputs(tx);
for (i = 0; i < rings.length; i++) {
ring = rings[i];
total += tx.template(ring);
}
callback(null, total);
});
@ -1404,15 +1409,15 @@ Wallet.prototype.sign = function sign(tx, options, callback) {
passphrase = options.passphrase;
timeout = options.timeout;
this.deriveInputs(tx, function(err, rings) {
this.unlock(passphrase, timeout, function(err, master) {
if (err)
return callback(err);
self.unlock(passphrase, timeout, function(err, master) {
self.deriveInputs(tx, master, function(err, rings) {
if (err)
return callback(err);
self.signAsync(rings, master, tx, callback);
self.signAsync(rings, tx, callback);
});
});
};
@ -1420,51 +1425,25 @@ Wallet.prototype.sign = function sign(tx, options, callback) {
/**
* Sign a transaction asynchronously.
* @param {KeyRing[]} rings
* @param {HDPrivateKey} master
* @param {MTX} tx
* @param {Number?} index
* @param {SighashType?} type
* @param {Function} callback - Returns [Error, Number] (total number
* of inputs scripts built and signed).
*/
Wallet.prototype.signAsync = function signAsync(rings, master, tx, callback) {
Wallet.prototype.signAsync = function signAsync(rings, tx, callback) {
var result;
if (!this.workerPool) {
callback = utils.asyncify(callback);
try {
result = Wallet.sign(rings, master, tx);
result = tx.sign(rings);
} catch (e) {
return callback(e);
}
return callback(null, result);
}
this.workerPool.sign(rings, master, tx, callback);
};
/**
* Sign a transaction.
* @param {KeyRing[]} rings
* @param {HDPrivateKey} master
* @param {MTX} tx
* @param {Number?} index
* @param {SighashType?} type
*/
Wallet.sign = function sign(rings, master, tx) {
var total = 0;
var i, ring, key;
for (i = 0; i < rings.length; i++) {
ring = rings[i];
key = ring.derive(master);
assert(utils.equal(key.getPublicKey(), ring.key));
total += ring.sign(tx, key);
}
return total;
this.workerPool.sign(tx, rings, null, callback);
};
/**
@ -2072,7 +2051,7 @@ Wallet.fromJSON = function fromJSON(db, json) {
Wallet.isWallet = function isWallet(obj) {
return obj
&& typeof obj.accountDepth === 'number'
&& obj.scriptInputs === 'function';
&& obj.template === 'function';
};
/**
@ -2120,7 +2099,7 @@ function Account(db, options) {
this.accountIndex = 0;
this.receiveDepth = 0;
this.changeDepth = 0;
this.type = keyTypes.PUBKEYHASH;
this.type = Account.types.PUBKEYHASH;
this.m = 1;
this.n = 1;
this.keys = [];
@ -2130,6 +2109,27 @@ function Account(db, options) {
this.fromOptions(options);
}
/**
* Account types.
* @enum {Number}
* @default
*/
Account.types = {
PUBKEYHASH: 0,
MULTISIG: 1
};
/**
* Account types by value.
* @const {RevMap}
*/
Account.typesByVal = {
0: 'pubkeyhash',
1: 'multisig'
};
/**
* Inject properties from options object.
* @private
@ -2177,12 +2177,12 @@ Account.prototype.fromOptions = function fromOptions(options) {
if (options.type != null) {
if (typeof options.type === 'string') {
this.type = keyTypes[options.type.toUpperCase()];
this.type = Account.types[options.type.toUpperCase()];
assert(this.type != null);
} else {
assert(typeof options.type === 'number');
this.type = options.type;
assert(keyTypesByVal[this.type]);
assert(Account.typesByVal[this.type]);
}
}
@ -2202,7 +2202,7 @@ Account.prototype.fromOptions = function fromOptions(options) {
}
if (this.n > 1)
this.type = keyTypes.MULTISIG;
this.type = Account.types.MULTISIG;
if (this.m < 1 || this.m > this.n)
throw new Error('m ranges between 1 and n');
@ -2384,7 +2384,7 @@ Account.prototype._checkKeys = function _checkKeys(callback) {
var self = this;
var ring, hash;
if (this.initialized || this.type !== keyTypes.MULTISIG)
if (this.initialized || this.type !== Account.types.MULTISIG)
return callback(null, false);
if (this.keys.length !== this.n - 1)
@ -2486,8 +2486,8 @@ Account.prototype.createAddress = function createAddress(change, callback) {
* @returns {KeyRing}
*/
Account.prototype.deriveReceive = function deriveReceive(index) {
return this.deriveAddress(false, index);
Account.prototype.deriveReceive = function deriveReceive(index, master) {
return this.deriveAddress(false, index, master);
};
/**
@ -2496,8 +2496,8 @@ Account.prototype.deriveReceive = function deriveReceive(index) {
* @returns {KeyRing}
*/
Account.prototype.deriveChange = function deriveChange(index) {
return this.deriveAddress(true, index);
Account.prototype.deriveChange = function deriveChange(index, master) {
return this.deriveAddress(true, index, master);
};
/**
@ -2507,13 +2507,20 @@ Account.prototype.deriveChange = function deriveChange(index) {
* @returns {KeyRing}
*/
Account.prototype.deriveAddress = function deriveAddress(change, index) {
Account.prototype.deriveAddress = function deriveAddress(change, index, master) {
var keys = [];
var i, key, shared;
var i, key, shared, ring;
change = +change;
key = this.accountKey.derive(change).derive(index);
if (master) {
key = master.deriveAccount44(this.accountIndex);
key = key.derive(change).derive(index);
} else {
key = this.accountKey.derive(change).derive(index);
}
keys.push(key.publicKey);
for (i = 0; i < this.keys.length; i++) {
shared = this.keys[i];
@ -2521,7 +2528,12 @@ Account.prototype.deriveAddress = function deriveAddress(change, index) {
keys.push(shared.publicKey);
}
return bcoin.keyring.fromAccount(this, key, keys, change, index);
ring = bcoin.keyring.fromAccount(this, key, keys, change, index);
if (master)
ring.privateKey = key.privateKey;
return ring;
};
/**
@ -2607,7 +2619,7 @@ Account.prototype.inspect = function inspect() {
name: this.name,
network: this.network,
initialized: this.initialized,
type: keyTypesByVal[this.type].toLowerCase(),
type: Account.typesByVal[this.type].toLowerCase(),
m: this.m,
n: this.n,
address: this.initialized
@ -2639,7 +2651,7 @@ Account.prototype.toJSON = function toJSON() {
wid: this.wid,
name: this.name,
initialized: this.initialized,
type: keyTypesByVal[this.type].toLowerCase(),
type: Account.typesByVal[this.type].toLowerCase(),
m: this.m,
n: this.n,
witness: this.witness,
@ -2688,7 +2700,7 @@ Account.prototype.fromJSON = function fromJSON(json) {
this.wid = json.wid;
this.name = json.name;
this.initialized = json.initialized;
this.type = keyTypes[json.type.toUpperCase()];
this.type = Account.types[json.type.toUpperCase()];
this.m = json.m;
this.n = json.n;
this.witness = json.witness;
@ -2763,7 +2775,7 @@ Account.prototype.fromRaw = function fromRaw(data) {
this.changeDepth = p.readU32();
this.accountKey = bcoin.hd.fromRaw(p.readBytes(82));
assert(keyTypesByVal[this.type]);
assert(Account.typesByVal[this.type]);
count = p.readU8();

View File

@ -1673,7 +1673,7 @@ Path.prototype.fromKeyRing = function fromKeyRing(ring) {
this.index = ring.index;
this.version = ring.witness ? 0 : -1;
this.type = ring.getScriptType();
this.type = ring.getType();
this.id = ring.id;
this.hash = ring.getHash('hex');

View File

@ -248,11 +248,10 @@ Workers.prototype.verify = function verify(tx, flags, callback) {
* @param {Function} callback
*/
Workers.prototype.sign = function sign(rings, master, tx, callback) {
var args = [rings, master, tx];
Workers.prototype.sign = function sign(tx, ring, type, callback) {
var i, input, sig, sigs, total;
this.execute('sign', args, -1, function(err, result) {
this.execute('sign', [tx, ring, type], -1, function(err, result) {
if (err)
return callback(err);
@ -775,8 +774,8 @@ jobs.verify = function verify(tx, flags) {
* @param {MTX} tx
*/
jobs.sign = function sign(rings, master, tx) {
var total = bcoin.wallet.sign(rings, master, tx);
jobs.sign = function sign(tx, ring, type) {
var total = tx.sign(ring, type);
var sigs = [];
var i, input;

View File

@ -239,7 +239,7 @@ describe('Wallet', function() {
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 500);
// Script inputs but do not sign
w.scriptInputs(fake, function(err) {
w.template(fake, function(err) {
assert.ifError(err);
// Fake signature
fake.inputs[0].script.set(0, FAKE_SIG);