fix p2sh multisig verification.
This commit is contained in:
parent
7cae4c4eb5
commit
6411ec8914
@ -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;
|
||||
|
||||
@ -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)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user