scripts: add allowIncomplete for multisig scripts

This commit is contained in:
Daniel Cousens 2015-02-11 14:01:20 +11:00
parent abf870fb37
commit c35d4b46c5
4 changed files with 118 additions and 23 deletions

View file

@ -1,6 +1,6 @@
var assert = require('assert')
var typeForce = require('typeforce')
var ops = require('./opcodes')
var typeForce = require('typeforce')
var ecurve = require('ecurve')
var curve = ecurve.getCurveByName('secp256k1')
@ -63,7 +63,7 @@ function isPubKeyOutput(script) {
script.chunks[1] === ops.OP_CHECKSIG
}
function isScriptHashInput(script) {
function isScriptHashInput(script, allowIncomplete) {
if (script.chunks.length < 2) return false
var lastChunk = script.chunks[script.chunks.length - 1]
@ -72,7 +72,7 @@ function isScriptHashInput(script) {
var scriptSig = Script.fromChunks(script.chunks.slice(0, -1))
var scriptPubKey = Script.fromBuffer(lastChunk)
return classifyInput(scriptSig) === classifyOutput(scriptPubKey)
return classifyInput(scriptSig, allowIncomplete) === classifyOutput(scriptPubKey)
}
function isScriptHashOutput(script) {
@ -83,9 +83,19 @@ function isScriptHashOutput(script) {
script.chunks[2] === ops.OP_EQUAL
}
function isMultisigInput(script) {
return script.chunks[0] === ops.OP_0 &&
script.chunks.slice(1).every(isCanonicalSignature)
// allowIncomplete is to account for combining signatures
// See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197
function isMultisigInput(script, allowIncomplete) {
if (script.chunks.length < 2) return false
if (script.chunks[0] !== ops.OP_0) return false
if (allowIncomplete) {
return script.chunks.slice(1).every(function(chunk) {
return chunk === ops.OP_0 || isCanonicalSignature(chunk)
})
}
return script.chunks.slice(1).every(isCanonicalSignature)
}
function isMultisigOutput(script) {
@ -134,15 +144,15 @@ function classifyOutput(script) {
return 'nonstandard'
}
function classifyInput(script) {
function classifyInput(script, allowIncomplete) {
typeForce('Script', script)
if (isPubKeyHashInput(script)) {
return 'pubkeyhash'
} else if (isScriptHashInput(script)) {
return 'scripthash'
} else if (isMultisigInput(script)) {
} else if (isMultisigInput(script, allowIncomplete)) {
return 'multisig'
} else if (isScriptHashInput(script, allowIncomplete)) {
return 'scripthash'
} else if (isPubKeyInput(script)) {
return 'pubkey'
}
@ -234,8 +244,13 @@ function multisigInput(signatures, scriptPubKey) {
var m = mOp - (ops.OP_1 - 1)
var n = nOp - (ops.OP_1 - 1)
assert(signatures.length >= m, 'Not enough signatures provided')
assert(signatures.length <= n, 'Too many signatures provided')
var count = 0
signatures.forEach(function(signature) {
count += (signature !== ops.OP_0)
})
assert(count >= m, 'Not enough signatures provided')
assert(count <= n, 'Too many signatures provided')
}
return Script.fromChunks([].concat(ops.OP_0, signatures))

View file

@ -1,4 +1,5 @@
var assert = require('assert')
var ops = require('./opcodes')
var scripts = require('./scripts')
var ECPubKey = require('./ecpubkey')
@ -44,23 +45,23 @@ TransactionBuilder.fromTransaction = function(transaction) {
var redeemScript
var scriptSig = txIn.script
var scriptType = scripts.classifyInput(scriptSig)
var scriptType = scripts.classifyInput(scriptSig, true)
// Re-classify if P2SH
if (scriptType === 'scripthash') {
redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
scriptType = scripts.classifyInput(scriptSig)
scriptType = scripts.classifyInput(scriptSig, true)
assert.equal(scripts.classifyOutput(redeemScript), scriptType, 'Non-matching scriptSig and scriptPubKey in input')
}
// Extract hashType, pubKeys and signatures
var hashType, pubKeys, signatures
var hashType, parsed, pubKeys, signatures
switch (scriptType) {
case 'pubkeyhash':
var parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
var pubKey = ECPubKey.fromBuffer(scriptSig.chunks[1])
hashType = parsed.hashType
@ -70,10 +71,9 @@ TransactionBuilder.fromTransaction = function(transaction) {
break
case 'multisig':
var scriptSigs = scriptSig.chunks.slice(1) // ignore OP_0
var parsed = scriptSigs.map(function(scriptSig) {
return ECSignature.parseScriptSignature(scriptSig)
})
parsed = scriptSig.chunks.slice(1).filter(function(chunk) {
return chunk !== ops.OP_0
}).map(ECSignature.parseScriptSignature)
hashType = parsed[0].hashType
pubKeys = []
@ -82,7 +82,7 @@ TransactionBuilder.fromTransaction = function(transaction) {
break
case 'pubkey':
var parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
hashType = parsed.hashType
pubKeys = []