fix p2sh multisig verification.

This commit is contained in:
Christopher Jeffrey 2015-12-19 01:43:34 -08:00
parent 7cae4c4eb5
commit 6411ec8914
2 changed files with 57 additions and 43 deletions

View File

@ -180,7 +180,13 @@ script.subscript = function subscript(s, lastSep) {
};
script.verify = function verify(hash, sig, pub) {
var k = bcoin.ecdsa.keyFromPublic(pub);
var k;
try {
k = bcoin.ecdsa.keyFromPublic(pub);
} catch (e) {
return false;
}
// Points at Infinity make verify() throw.
// This specifically throws on wallet-test.js
@ -240,7 +246,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
var n, n1, n2, n3;
var res;
var pub, sig, type, subscript, hash;
var keys, i, key, m;
var keys, i, j, key, m;
var succ;
var lock, threshold;
var evalScript;
@ -720,7 +726,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
// Get signatures
succ = 0;
for (i = 0; i < m; i++) {
for (i = 0, j = 0; i < m && j < n; i++) {
sig = stack.pop();
type = sig[sig.length - 1];
if (!constants.rhashType[type & 0x1f])
@ -731,8 +737,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
hash = tx.subscriptHash(index, subscript, type);
// Strict order:
res = script.verify(hash, sig.slice(0, -1), keys.pop());
res = false;
for (; !res && j < n; j++)
res = script.verify(hash, sig.slice(0, -1), keys[j]);
if (res)
succ++;
}
@ -740,6 +748,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
// Extra value
stack.pop();
// Too many signatures on stack
// if (stack.length > 0)
// return false;
res = succ >= m;
if (o === 'checkmultisigverify') {
if (!res)
@ -826,8 +838,7 @@ script.exec = function(input, output, tx, i, recurse) {
res = script.execute(output, stack, tx, i, recurse);
// if (!res || stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0)
if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false;
return true;
@ -1204,7 +1215,11 @@ script.format = function(input, output) {
var scripts = [];
var prev, redeem;
if (input) {
if (Array.isArray(input)) {
scripts.push(input);
} else if (Array.isArray(output)) {
scripts.push(output);
} else if (input) {
scripts.push(input.script);
if (input.out.tx && input.out.tx.outputs[input.out.index]) {
prev = input.out.tx.outputs[input.out.index].script;

View File

@ -177,8 +177,13 @@ TX.prototype.prevOut = function prevOut(i, def) {
return input.out.tx.outputs[input.out.index] || def;
};
TX.prototype.signatureHash = function signatureHash(i, type) {
var input, s, hash;
TX.prototype.signatureHash = function signatureHash(i, s, type) {
var input, hash;
if (s != null && !Array.isArray(s)) {
type = s;
s = null;
}
if (typeof i === 'object')
i = this.inputs.indexOf(i);
@ -193,7 +198,7 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
input = this.inputs[i];
// Get the previous output's subscript
s = input.out.tx.getSubscript(input.out.index);
s = s || input.out.tx.getSubscript(input.out.index);
// Get the hash of the current tx, minus the other inputs, plus the sighash.
hash = this.subscriptHash(i, s, type);
@ -201,9 +206,14 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
return hash;
};
TX.prototype.signature = function signature(i, key, type) {
TX.prototype.signature = function signature(i, key, s, type) {
var hash, signature;
if (s != null && !Array.isArray(s)) {
type = s;
s = null;
}
if (typeof i === 'object')
i = this.inputs.indexOf(i);
@ -214,7 +224,7 @@ TX.prototype.signature = function signature(i, key, type) {
type = constants.hashType[type];
// Get the hash of the current tx, minus the other inputs, plus the sighash.
hash = this.sigHash(i, type);
hash = this.signatureHash(i, s, type);
// Sign the transaction with our one input
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
@ -300,8 +310,15 @@ TX.prototype.signInput = function signInput(input, key, type) {
// Get the previous output's subscript
s = input.out.tx.getSubscript(input.out.index);
if (bcoin.script.isScripthash(s)) {
// We need to grab the redeem script when signing p2sh transactions.
redeem = bcoin.script.decode(input.script[input.script.length - 1]);
} else {
redeem = s;
}
// Get the hash of the current tx, minus the other inputs, plus the sighash.
hash = this.subscriptHash(this.inputs.indexOf(input), s, type);
hash = this.subscriptHash(this.inputs.indexOf(input), redeem, type);
// Sign the transaction with our one input
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
@ -327,12 +344,8 @@ TX.prototype.signInput = function signInput(input, key, type) {
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
len = input.script.length;
if (bcoin.script.isScripthash(s)) {
if (bcoin.script.isScripthash(s))
len--;
redeem = bcoin.script.decode(input.script[input.script.length - 1]);
} else {
redeem = s;
}
m = redeem[0];
// If using pushdata instead of OP_1-16:
@ -468,7 +481,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
n = options.n || keys.length;
assert(m >= 1 && m <= n);
if (options.hash)
if (options.scripthash)
assert(n >= 1 && n <= 15);
else
assert(n >= 1 && n <= 3);
@ -613,12 +626,8 @@ TX.prototype.verify = function verify(index, force) {
bcoin.script.execute(input.script, stack, this, i);
res = bcoin.script.execute(prev, stack, this, i);
if (!res)
return false;
// Might be necessary for arithmetic:
// if (stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0)
if (stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false;
if (bcoin.script.isScripthash(prev)) {
@ -627,7 +636,7 @@ TX.prototype.verify = function verify(index, force) {
return false;
redeem = bcoin.script.decode(redeem);
res = bcoin.script.execute(redeem, stack, this, i);
if (!res)
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false;
}
@ -728,15 +737,6 @@ TX.prototype.maxSize = function maxSize() {
return size;
};
// Building a TX:
// 1. Add outputs:
// - this.output({ address: ..., value: ... });
// - this.output({ address: ..., value: ... });
// 2. Add inputs with utxos and change output:
// - this.fillUnspent(unspentItems, [changeAddr]);
// 3. Fill input scripts (for each input):
// - this.scriptInput(input, pub)
// - this.signInput(input, key, [sigHashType])
TX.prototype.utxos = function utxos(unspent) {
// NOTE: tx should be prefilled with all outputs
var cost = this.funds('out');
@ -826,8 +826,6 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) {
if (result.change.cmpn(TX.dust) < 0) {
// Do nothing. Change is added to fee.
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
// Adding change to outputs.
// this.outputs[this.outputs.length - 1].value.iadd(result.change);
this.changeOutput = null;
} else {
this.output({
@ -895,16 +893,15 @@ TX.getInputData = function getInputData(input) {
if (!input || !input.script) return;
var script = input.script;
var scriptSig, pub, hash, addr, redeem, data;
var output;
var sig, pub, hash, addr, redeem, data, output;
if (bcoin.script.isPubkeyhashInput(script)) {
scriptSig = utils.toHex(script[0]);
sig = utils.toHex(script[0]);
pub = script[1];
hash = utils.ripesha(pub);
addr = bcoin.wallet.hash2addr(hash);
return {
sig: scriptSig,
sig: sig,
pub: pub,
hash: hash,
addr: addr
@ -946,7 +943,7 @@ TX.getOutputData = function getOutputData(output) {
if (!output || !output.script) return;
var script = output.script;
var pub, hash, addr, pubs;
var pub, hash, addr, pubs, ret;
if (bcoin.script.isPubkey(script)) {
pub = script[0];
@ -1014,8 +1011,10 @@ TX.getOutputData = function getOutputData(output) {
}
if (bcoin.script.isColored(script)) {
ret = bcoin.script.colored(script);
return {
data: bcoin.script.colored(script)
data: ret,
text: utils.array2ascii(ret)
};
}
};