mtx: signing work.
This commit is contained in:
parent
b6c8362c63
commit
a42d11cb80
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
217
lib/bcoin/mtx.js
217
lib/bcoin/mtx.js
@ -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';
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user