mtx: signing work.

This commit is contained in:
Christopher Jeffrey 2016-08-18 18:27:17 -07:00
parent b6c8362c63
commit a42d11cb80
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 161 additions and 102 deletions

View File

@ -623,16 +623,7 @@ KeyRing.prototype.getRedeem = function(hash) {
*/
KeyRing.prototype.scriptInputs = function scriptInputs(tx) {
var total = 0;
var i, input;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (tx.scriptInput(i, this))
total++;
}
return total;
return tx.template(this.publicKey, this.script, this.program);
};
/**
@ -644,16 +635,7 @@ KeyRing.prototype.scriptInputs = function scriptInputs(tx) {
*/
KeyRing.prototype.sign = function sign(tx, key) {
var total = 0;
var i, input;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (tx.sign(i, this, key))
total++;
}
return total;
return tx.sign(key, this.script, this.program);
};
/**

View File

@ -217,9 +217,28 @@ MTX.prototype.addOutput = function addOutput(options, value) {
* @throws on unavailable coins.
*/
MTX.prototype.scriptInput = function scriptInput(index, ring) {
function getRedeem(hash, script, program) {
if (program) {
if (utils.equal(program.hash160(), hash))
return program;
}
if (script) {
if (utils.equal(script.hash160(), hash))
return script;
if (utils.equal(script.sha256(), hash))
return script;
}
};
MTX.prototype.buildInput = function buildInput(index, key, script, program) {
var input, prev, redeem;
if (key.getPublicKey)
key = key.getPublicKey();
// Get the input
input = this.inputs[index];
assert(input);
@ -233,10 +252,6 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
if (input.script.length !== 0 || input.witness.length !== 0)
return true;
// Make sure this coin is ours.
if (!ring.ownOutput(input.coin))
return false;
// Get the previous output's script
prev = input.coin.script;
@ -244,7 +259,7 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
// with segwit: figuring out where the redeem script and witness
// redeem scripts go.
if (prev.isScripthash()) {
redeem = ring.getRedeem(prev.get(1));
redeem = getRedeem(prev.get(1), script, program);
if (!redeem)
return false;
@ -253,10 +268,10 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
if (redeem.isProgram()) {
// P2WSH nested within pay-to-scripthash.
if (redeem.isWitnessScripthash()) {
prev = ring.getRedeem(redeem.get(1));
prev = getRedeem(redeem.get(1), script, program);
if (!prev)
return false;
this.scriptVector(prev, input.witness, ring);
this.scriptVector(prev, input.witness, key);
input.witness.push(prev.toRaw());
input.script.push(redeem.toRaw());
input.script.compile();
@ -265,8 +280,8 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
// P2WPKH nested within pay-to-scripthash.
if (redeem.isWitnessPubkeyhash()) {
prev = Script.fromPubkeyhash(ring.keyHash);
this.scriptVector(prev, input.witness, ring);
prev = Script.fromPubkeyhash(utils.hash160(key));
this.scriptVector(prev, input.witness, key);
input.script.push(redeem.toRaw());
input.script.compile();
return true;
@ -277,7 +292,7 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
}
// Regular P2SH.
this.scriptVector(redeem, input.script, ring);
this.scriptVector(redeem, input.script, key);
input.script.push(redeem.toRaw());
input.script.compile();
return true;
@ -287,12 +302,12 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
if (prev.isProgram()) {
// Bare P2WSH.
if (prev.isWitnessScripthash()) {
redeem = ring.getRedeem(prev.get(1));
redeem = getRedeem(prev.get(1), script, program);
if (!redeem)
return false;
this.scriptVector(redeem, input.witness, ring);
this.scriptVector(redeem, input.witness, key);
input.witness.push(redeem.toRaw());
input.script.compile();
return true;
@ -301,7 +316,7 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
// Bare P2WPKH.
if (prev.isWitnessPubkeyhash()) {
prev = Script.fromPubkeyhash(prev.get(1));
this.scriptVector(prev, input.witness, ring);
this.scriptVector(prev, input.witness, key);
input.script.compile();
return true;
}
@ -311,16 +326,16 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) {
}
// Wow, a normal output! Praise be to Jengus and Gord.
this.scriptVector(prev, input.script, ring);
this.scriptVector(prev, input.script, key);
return true;
};
MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
MTX.prototype.scriptVector = function scriptVector(prev, vector, key) {
var i, n;
// P2PK
if (prev.isPubkey()) {
if (!utils.equal(prev.get(1), ring.publicKey))
if (!utils.equal(prev.get(1), key))
return false;
// Already has a script template (at least)
@ -334,7 +349,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
// P2PKH
if (prev.isPubkeyhash()) {
if (!utils.equal(prev.get(2), ring.keyHash))
if (!utils.equal(prev.get(2), utils.hash160(key)))
return false;
// Already has a script template (at least)
@ -342,14 +357,14 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
return true;
vector.set(0, opcodes.OP_0);
vector.set(1, ring.publicKey);
vector.set(1, key);
return true;
}
// Multisig
if (prev.isMultisig()) {
if (prev.indexOf(ring.publicKey) === -1)
if (prev.indexOf(key) === -1)
return false;
// Already has a script template (at least)
@ -371,7 +386,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
return true;
}
if (prev.indexOf(ring.publicKey) === -1)
if (prev.indexOf(key) === -1)
return false;
// Already has a script template (at least)
@ -391,34 +406,6 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
}
};
/**
* Create a signature suitable for inserting into scriptSigs/witnesses.
* @param {Number} index - Index of input being signed.
* @param {Script} prev - Previous output script or redeem script
* (in the case of witnesspubkeyhash, this should be the generated
* p2pkh script).
* @param {SighashType} type
* @param {Number} version - Sighash version (0=legacy, 1=segwit).
* @returns {Buffer} Signature in DER format.
*/
MTX.prototype.createSignature = function createSignature(index, prev, key, type, version) {
var hash;
if (type == null)
type = constants.hashType.ALL;
if (typeof type === 'string')
type = constants.hashType[type.toUpperCase()];
// Get the hash of the current tx, minus the other
// inputs, plus the sighash type.
hash = this.signatureHash(index, prev, type, version);
// Sign the transaction with our one input
return Script.sign(hash, key, type);
};
/**
* Sign an input.
* @param {Number} index - Index of input being signed.
@ -430,22 +417,21 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type,
* @throws on unavailable coins.
*/
MTX.prototype.signInput = function signInput(index, ring, key, type) {
MTX.prototype.signInput = function signInput(index, key, type) {
var input = this.inputs[index];
var version = 0;
var redeem = false;
var prev, vector, signature, result;
var prev, vector, sig, result;
assert(input);
if (key.getPrivateKey)
key = key.getPrivateKey();
// We should have previous outputs by now.
if (!input.coin)
return false;
// Make sure this output is ours.
if (!ring.ownOutput(input.coin))
return false;
// Get the previous output's script
prev = input.coin.script;
vector = input.script;
@ -479,36 +465,37 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) {
}
// Create our signature.
signature = this.createSignature(index, prev, key, type, version);
sig = this.createSignature(index, prev, key, type, version);
if (redeem) {
redeem = vector.pop();
result = this.signVector(prev, vector, signature, ring);
result = this.signVector(prev, vector, sig, key);
vector.push(redeem);
vector.compile();
return result;
}
return this.signVector(prev, vector, signature, ring);
return this.signVector(prev, vector, sig, key);
};
MTX.prototype.signVector = function signVector(prev, vector, signature, ring) {
MTX.prototype.signVector = function signVector(prev, vector, sig, key) {
var pub = bcoin.ec.publicKeyCreate(key, true);
var keys, i, m, n, signatures, keyIndex;
// P2PK
if (prev.isPubkey()) {
// Make sure the pubkey is ours.
if (!utils.equal(pub, prev.get(0)))
return false;
// Already signed.
if (Script.isSignature(vector.get(0)))
return true;
// Make sure the pubkey is ours.
if (!utils.equal(ring.publicKey, prev.get(0)))
return false;
if (vector.getSmall(0) !== 0)
throw new Error('Input has not been templated.');
vector.set(0, signature);
vector.set(0, sig);
vector.compile();
return true;
@ -516,18 +503,18 @@ MTX.prototype.signVector = function signVector(prev, vector, signature, ring) {
// P2PKH
if (prev.isPubkeyhash()) {
// Make sure the pubkey hash is ours.
if (!utils.equal(utils.hash160(pub), prev.get(2)))
return false;
// Already signed.
if (Script.isSignature(vector.get(0)))
return true;
// Make sure the pubkey hash is ours.
if (!utils.equal(ring.keyHash, prev.get(2)))
return false;
if (!Script.isKey(vector.get(1)))
throw new Error('Input has not been templated.');
vector.set(0, signature);
vector.set(0, sig);
vector.compile();
return true;
@ -586,7 +573,7 @@ MTX.prototype.signVector = function signVector(prev, vector, signature, ring) {
// Find the key index so we can place
// the signature in the same index.
keyIndex = utils.indexOf(keys, ring.publicKey);
keyIndex = utils.indexOf(keys, pub);
// Our public key is not in the prev_out
// script. We tried to sign a transaction
@ -604,7 +591,7 @@ MTX.prototype.signVector = function signVector(prev, vector, signature, ring) {
// signatures.
if (keyIndex < vector.length && signatures < m) {
if (vector.getSmall(keyIndex) === 0) {
vector.set(keyIndex, signature);
vector.set(keyIndex, sig);
signatures++;
}
}
@ -688,6 +675,35 @@ MTX.prototype.combineMultisig = function combineMultisig(index, prev, version, s
return result;
};
/**
* Create a signature suitable for inserting into scriptSigs/witnesses.
* @param {Number} index - Index of input being signed.
* @param {Script} prev - Previous output script or redeem script
* (in the case of witnesspubkeyhash, this should be the generated
* p2pkh script).
* @param {SighashType} type
* @param {Number} version - Sighash version (0=legacy, 1=segwit).
* @returns {Buffer} Signature in DER format.
*/
MTX.prototype.createSignature = function createSignature(index, prev, key, type, version) {
var hash;
if (type == null)
type = constants.hashType.ALL;
if (typeof type === 'string')
type = constants.hashType[type.toUpperCase()];
// Get the hash of the current tx, minus the other
// inputs, plus the sighash type.
hash = this.signatureHash(index, prev, type, version);
// Sign the transaction with our one input
return Script.sign(hash, key, type);
};
/**
* Test whether the transaction is fully-signed.
* @returns {Boolean}
@ -774,19 +790,56 @@ MTX.prototype.isSigned = function isSigned() {
* @throws on unavailable coins.
*/
MTX.prototype.sign = function sign(index, ring, key, type) {
var input = this.inputs[index];
assert(input);
MTX.prototype.template = function template(key, script, program, type) {
var total = 0;
var i;
// Build script for input
if (!this.scriptInput(index, ring))
return false;
if (key.getPublicKey)
key = key.getPublicKey();
// Sign input
if (!this.signInput(index, ring, key, type))
return false;
for (i = 0; i < this.inputs.length; i++) {
// Build script for input
if (!this.buildInput(i, key, script, program))
continue;
total++;
}
return true;
return total;
};
/**
* 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, program, type) {
var total = 0;
var i, pub;
if (key.getPrivateKey)
key = key.getPrivateKey();
pub = bcoin.ec.publicKeyCreate(key, true);
for (i = 0; i < this.inputs.length; i++) {
// Build script for input
if (!this.buildInput(i, pub, script, program))
continue;
// Sign input
if (!this.signInput(i, key, type))
continue;
total++;
}
return total;
};
/**
@ -1412,7 +1465,7 @@ MTX.isMTX = function isMTX(obj) {
return obj
&& Array.isArray(obj.inputs)
&& typeof obj.locktime === 'number'
&& typeof obj.scriptInput === 'function';
&& typeof obj.buildInput === 'function';
};
/*

View File

@ -2846,6 +2846,30 @@ Script.prototype.getAddress = function getAddress() {
return bcoin.address.fromScript(this);
};
/**
* Get the hash160 of the raw script.
* @param {Buffer}
*/
Script.prototype.hash160 = function hash160(enc) {
var hash = utils.hash160(this.toRaw());
if (enc === 'hex')
hash = hash.toString('hex');
return hash;
};
/**
* Get the sha256 of the raw script.
* @param {Buffer}
*/
Script.prototype.sha256 = function sha256(enc) {
var hash = utils.sha256(this.toRaw());
if (enc === 'hex')
hash = hash.toString('hex');
return hash;
};
/**
* Test whether the output script is pay-to-pubkey.
* @param {Boolean} [minimal=false] - Minimaldata only.