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) {
|
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.
|
// Points at Infinity make verify() throw.
|
||||||
// This specifically throws on wallet-test.js
|
// 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 n, n1, n2, n3;
|
||||||
var res;
|
var res;
|
||||||
var pub, sig, type, subscript, hash;
|
var pub, sig, type, subscript, hash;
|
||||||
var keys, i, key, m;
|
var keys, i, j, key, m;
|
||||||
var succ;
|
var succ;
|
||||||
var lock, threshold;
|
var lock, threshold;
|
||||||
var evalScript;
|
var evalScript;
|
||||||
@ -720,7 +726,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
|
|
||||||
// Get signatures
|
// Get signatures
|
||||||
succ = 0;
|
succ = 0;
|
||||||
for (i = 0; i < m; i++) {
|
for (i = 0, j = 0; i < m && j < n; i++) {
|
||||||
sig = stack.pop();
|
sig = stack.pop();
|
||||||
type = sig[sig.length - 1];
|
type = sig[sig.length - 1];
|
||||||
if (!constants.rhashType[type & 0x1f])
|
if (!constants.rhashType[type & 0x1f])
|
||||||
@ -731,8 +737,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
|
|
||||||
hash = tx.subscriptHash(index, subscript, type);
|
hash = tx.subscriptHash(index, subscript, type);
|
||||||
|
|
||||||
// Strict order:
|
res = false;
|
||||||
res = script.verify(hash, sig.slice(0, -1), keys.pop());
|
for (; !res && j < n; j++)
|
||||||
|
res = script.verify(hash, sig.slice(0, -1), keys[j]);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
succ++;
|
succ++;
|
||||||
}
|
}
|
||||||
@ -740,6 +748,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
// Extra value
|
// Extra value
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
|
||||||
|
// Too many signatures on stack
|
||||||
|
// if (stack.length > 0)
|
||||||
|
// return false;
|
||||||
|
|
||||||
res = succ >= m;
|
res = succ >= m;
|
||||||
if (o === 'checkmultisigverify') {
|
if (o === 'checkmultisigverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
@ -826,8 +838,7 @@ script.exec = function(input, output, tx, i, recurse) {
|
|||||||
|
|
||||||
res = script.execute(output, stack, 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 || new bn(stack.pop()).cmpn(0) === 0)
|
||||||
if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1204,7 +1215,11 @@ script.format = function(input, output) {
|
|||||||
var scripts = [];
|
var scripts = [];
|
||||||
var prev, redeem;
|
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);
|
scripts.push(input.script);
|
||||||
if (input.out.tx && input.out.tx.outputs[input.out.index]) {
|
if (input.out.tx && input.out.tx.outputs[input.out.index]) {
|
||||||
prev = input.out.tx.outputs[input.out.index].script;
|
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;
|
return input.out.tx.outputs[input.out.index] || def;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signatureHash = function signatureHash(i, type) {
|
TX.prototype.signatureHash = function signatureHash(i, s, type) {
|
||||||
var input, s, hash;
|
var input, hash;
|
||||||
|
|
||||||
|
if (s != null && !Array.isArray(s)) {
|
||||||
|
type = s;
|
||||||
|
s = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof i === 'object')
|
if (typeof i === 'object')
|
||||||
i = this.inputs.indexOf(i);
|
i = this.inputs.indexOf(i);
|
||||||
@ -193,7 +198,7 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
|
|||||||
input = this.inputs[i];
|
input = this.inputs[i];
|
||||||
|
|
||||||
// Get the previous output's subscript
|
// 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.
|
// Get the hash of the current tx, minus the other inputs, plus the sighash.
|
||||||
hash = this.subscriptHash(i, s, type);
|
hash = this.subscriptHash(i, s, type);
|
||||||
@ -201,9 +206,14 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
|
|||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signature = function signature(i, key, type) {
|
TX.prototype.signature = function signature(i, key, s, type) {
|
||||||
var hash, signature;
|
var hash, signature;
|
||||||
|
|
||||||
|
if (s != null && !Array.isArray(s)) {
|
||||||
|
type = s;
|
||||||
|
s = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof i === 'object')
|
if (typeof i === 'object')
|
||||||
i = this.inputs.indexOf(i);
|
i = this.inputs.indexOf(i);
|
||||||
|
|
||||||
@ -214,7 +224,7 @@ TX.prototype.signature = function signature(i, key, type) {
|
|||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
|
|
||||||
// Get the hash of the current tx, minus the other inputs, plus the sighash.
|
// 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
|
// Sign the transaction with our one input
|
||||||
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
|
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
|
// Get the previous output's subscript
|
||||||
s = input.out.tx.getSubscript(input.out.index);
|
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.
|
// 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
|
// Sign the transaction with our one input
|
||||||
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
|
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)) {
|
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
|
||||||
len = input.script.length;
|
len = input.script.length;
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s))
|
||||||
len--;
|
len--;
|
||||||
redeem = bcoin.script.decode(input.script[input.script.length - 1]);
|
|
||||||
} else {
|
|
||||||
redeem = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = redeem[0];
|
m = redeem[0];
|
||||||
// If using pushdata instead of OP_1-16:
|
// If using pushdata instead of OP_1-16:
|
||||||
@ -468,7 +481,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
n = options.n || keys.length;
|
n = options.n || keys.length;
|
||||||
|
|
||||||
assert(m >= 1 && m <= n);
|
assert(m >= 1 && m <= n);
|
||||||
if (options.hash)
|
if (options.scripthash)
|
||||||
assert(n >= 1 && n <= 15);
|
assert(n >= 1 && n <= 15);
|
||||||
else
|
else
|
||||||
assert(n >= 1 && n <= 3);
|
assert(n >= 1 && n <= 3);
|
||||||
@ -613,12 +626,8 @@ TX.prototype.verify = function verify(index, force) {
|
|||||||
|
|
||||||
bcoin.script.execute(input.script, stack, this, i);
|
bcoin.script.execute(input.script, stack, this, i);
|
||||||
res = bcoin.script.execute(prev, stack, this, i);
|
res = bcoin.script.execute(prev, stack, this, i);
|
||||||
if (!res)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Might be necessary for arithmetic:
|
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
||||||
// if (stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0)
|
|
||||||
if (stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(prev)) {
|
if (bcoin.script.isScripthash(prev)) {
|
||||||
@ -627,7 +636,7 @@ TX.prototype.verify = function verify(index, force) {
|
|||||||
return false;
|
return false;
|
||||||
redeem = bcoin.script.decode(redeem);
|
redeem = bcoin.script.decode(redeem);
|
||||||
res = bcoin.script.execute(redeem, stack, this, i);
|
res = bcoin.script.execute(redeem, stack, this, i);
|
||||||
if (!res)
|
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,15 +737,6 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
return size;
|
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) {
|
TX.prototype.utxos = function utxos(unspent) {
|
||||||
// NOTE: tx should be prefilled with all outputs
|
// NOTE: tx should be prefilled with all outputs
|
||||||
var cost = this.funds('out');
|
var cost = this.funds('out');
|
||||||
@ -826,8 +826,6 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) {
|
|||||||
if (result.change.cmpn(TX.dust) < 0) {
|
if (result.change.cmpn(TX.dust) < 0) {
|
||||||
// Do nothing. Change is added to fee.
|
// Do nothing. Change is added to fee.
|
||||||
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
|
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;
|
this.changeOutput = null;
|
||||||
} else {
|
} else {
|
||||||
this.output({
|
this.output({
|
||||||
@ -895,16 +893,15 @@ TX.getInputData = function getInputData(input) {
|
|||||||
if (!input || !input.script) return;
|
if (!input || !input.script) return;
|
||||||
|
|
||||||
var script = input.script;
|
var script = input.script;
|
||||||
var scriptSig, pub, hash, addr, redeem, data;
|
var sig, pub, hash, addr, redeem, data, output;
|
||||||
var output;
|
|
||||||
|
|
||||||
if (bcoin.script.isPubkeyhashInput(script)) {
|
if (bcoin.script.isPubkeyhashInput(script)) {
|
||||||
scriptSig = utils.toHex(script[0]);
|
sig = utils.toHex(script[0]);
|
||||||
pub = script[1];
|
pub = script[1];
|
||||||
hash = utils.ripesha(pub);
|
hash = utils.ripesha(pub);
|
||||||
addr = bcoin.wallet.hash2addr(hash);
|
addr = bcoin.wallet.hash2addr(hash);
|
||||||
return {
|
return {
|
||||||
sig: scriptSig,
|
sig: sig,
|
||||||
pub: pub,
|
pub: pub,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
addr: addr
|
addr: addr
|
||||||
@ -946,7 +943,7 @@ TX.getOutputData = function getOutputData(output) {
|
|||||||
if (!output || !output.script) return;
|
if (!output || !output.script) return;
|
||||||
|
|
||||||
var script = output.script;
|
var script = output.script;
|
||||||
var pub, hash, addr, pubs;
|
var pub, hash, addr, pubs, ret;
|
||||||
|
|
||||||
if (bcoin.script.isPubkey(script)) {
|
if (bcoin.script.isPubkey(script)) {
|
||||||
pub = script[0];
|
pub = script[0];
|
||||||
@ -1014,8 +1011,10 @@ TX.getOutputData = function getOutputData(output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isColored(script)) {
|
if (bcoin.script.isColored(script)) {
|
||||||
|
ret = bcoin.script.colored(script);
|
||||||
return {
|
return {
|
||||||
data: bcoin.script.colored(script)
|
data: ret,
|
||||||
|
text: utils.array2ascii(ret)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user