|
|
@ -44,86 +44,98 @@ function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, |
|
|
|
} |
|
|
|
|
|
|
|
function extractInput (transaction, txIn, vin) { |
|
|
|
var redeemScript |
|
|
|
var scriptSig = txIn.script |
|
|
|
var scriptSigChunks = bscript.decompile(scriptSig) |
|
|
|
var scriptSigChunks = bscript.decompile(txIn.script) |
|
|
|
var prevOutType = bscript.classifyInput(scriptSigChunks, true) |
|
|
|
|
|
|
|
var prevOutScript |
|
|
|
var prevOutType = bscript.classifyInput(scriptSig, true) |
|
|
|
var scriptType |
|
|
|
if (txIn.script.length === 0) { |
|
|
|
return {} |
|
|
|
} |
|
|
|
|
|
|
|
// Re-classify if scriptHash
|
|
|
|
if (prevOutType === 'scripthash') { |
|
|
|
redeemScript = scriptSigChunks.slice(-1)[0] |
|
|
|
prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) |
|
|
|
var processScript = function (scriptType, scriptSigChunks, redeemScriptChunks) { |
|
|
|
// ensure chunks are decompiled
|
|
|
|
scriptSigChunks = bscript.decompile(scriptSigChunks) |
|
|
|
redeemScriptChunks = redeemScriptChunks ? bscript.decompile(redeemScriptChunks) : undefined |
|
|
|
|
|
|
|
scriptSig = bscript.compile(scriptSigChunks.slice(0, -1)) |
|
|
|
scriptSigChunks = scriptSigChunks.slice(0, -1) |
|
|
|
var hashType, pubKeys, signatures, prevOutScript, redeemScript, redeemScriptType, result, parsed |
|
|
|
|
|
|
|
scriptType = bscript.classifyInput(scriptSig, true) |
|
|
|
} else { |
|
|
|
scriptType = prevOutType |
|
|
|
} |
|
|
|
switch (scriptType) { |
|
|
|
case 'scripthash': |
|
|
|
redeemScript = scriptSigChunks.slice(-1)[0] |
|
|
|
scriptSigChunks = bscript.compile(scriptSigChunks.slice(0, -1)) |
|
|
|
|
|
|
|
// pre-empt redeemScript decompilation
|
|
|
|
var redeemScriptChunks |
|
|
|
if (redeemScript) { |
|
|
|
redeemScriptChunks = bscript.decompile(redeemScript) |
|
|
|
} |
|
|
|
redeemScriptType = bscript.classifyInput(scriptSigChunks, true) |
|
|
|
prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) |
|
|
|
|
|
|
|
// Extract hashType, pubKeys and signatures
|
|
|
|
var hashType, parsed, pubKeys, signatures |
|
|
|
result = processScript(redeemScriptType, scriptSigChunks, bscript.decompile(redeemScript)) |
|
|
|
|
|
|
|
switch (scriptType) { |
|
|
|
case 'pubkeyhash': |
|
|
|
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) |
|
|
|
hashType = parsed.hashType |
|
|
|
pubKeys = scriptSigChunks.slice(1) |
|
|
|
signatures = [parsed.signature] |
|
|
|
prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0])) |
|
|
|
result.prevOutScript = prevOutScript |
|
|
|
result.redeemScript = redeemScript |
|
|
|
result.redeemScriptType = redeemScriptType |
|
|
|
|
|
|
|
break |
|
|
|
return result |
|
|
|
|
|
|
|
case 'pubkey': |
|
|
|
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) |
|
|
|
hashType = parsed.hashType |
|
|
|
signatures = [parsed.signature] |
|
|
|
case 'pubkeyhash': |
|
|
|
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) |
|
|
|
hashType = parsed.hashType |
|
|
|
pubKeys = scriptSigChunks.slice(1) |
|
|
|
signatures = [parsed.signature] |
|
|
|
prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0])) |
|
|
|
|
|
|
|
if (redeemScript) { |
|
|
|
pubKeys = redeemScriptChunks.slice(0, 1) |
|
|
|
} |
|
|
|
break |
|
|
|
|
|
|
|
break |
|
|
|
case 'pubkey': |
|
|
|
parsed = ECSignature.parseScriptSignature(scriptSigChunks[0]) |
|
|
|
hashType = parsed.hashType |
|
|
|
signatures = [parsed.signature] |
|
|
|
|
|
|
|
case 'multisig': |
|
|
|
signatures = scriptSigChunks.slice(1).map(function (chunk) { |
|
|
|
if (chunk === ops.OP_0) return undefined |
|
|
|
if (redeemScriptChunks) { |
|
|
|
pubKeys = redeemScriptChunks.slice(0, 1) |
|
|
|
} |
|
|
|
|
|
|
|
var parsed = ECSignature.parseScriptSignature(chunk) |
|
|
|
hashType = parsed.hashType |
|
|
|
break |
|
|
|
|
|
|
|
case 'multisig': |
|
|
|
signatures = scriptSigChunks.slice(1).map(function (chunk) { |
|
|
|
if (chunk === ops.OP_0) return undefined |
|
|
|
|
|
|
|
parsed = ECSignature.parseScriptSignature(chunk) |
|
|
|
hashType = parsed.hashType |
|
|
|
|
|
|
|
return parsed.signature |
|
|
|
}) |
|
|
|
return parsed.signature |
|
|
|
}) |
|
|
|
|
|
|
|
if (redeemScript) { |
|
|
|
pubKeys = redeemScriptChunks.slice(1, -2) |
|
|
|
if (redeemScriptChunks) { |
|
|
|
pubKeys = redeemScriptChunks.slice(1, -2) |
|
|
|
|
|
|
|
if (pubKeys.length !== signatures.length) { |
|
|
|
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, redeemScript, hashType, redeemScript) |
|
|
|
if (pubKeys.length !== signatures.length) { |
|
|
|
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, bscript.compile(redeemScriptChunks), hashType, redeemScript) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
hashType: hashType, |
|
|
|
pubKeys: pubKeys, |
|
|
|
signatures: signatures, |
|
|
|
prevOutScript: prevOutScript, |
|
|
|
redeemScript: redeemScript, |
|
|
|
redeemScriptType: redeemScriptType |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Extract hashType, pubKeys, signatures and prevOutScript
|
|
|
|
var result = processScript(prevOutType, scriptSigChunks) |
|
|
|
|
|
|
|
return { |
|
|
|
hashType: hashType, |
|
|
|
prevOutScript: prevOutScript, |
|
|
|
hashType: result.hashType, |
|
|
|
prevOutScript: result.prevOutScript, |
|
|
|
prevOutType: prevOutType, |
|
|
|
pubKeys: pubKeys, |
|
|
|
redeemScript: redeemScript, |
|
|
|
scriptType: scriptType, |
|
|
|
signatures: signatures |
|
|
|
pubKeys: result.pubKeys, |
|
|
|
redeemScript: result.redeemScript, |
|
|
|
redeemScriptType: result.redeemScriptType, |
|
|
|
signatures: result.signatures |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -176,9 +188,6 @@ TransactionBuilder.fromTransaction = function (transaction, network) { |
|
|
|
throw new Error('coinbase inputs not supported') |
|
|
|
} |
|
|
|
|
|
|
|
// Ignore empty scripts
|
|
|
|
if (txIn.script.length === 0) return {} |
|
|
|
|
|
|
|
return extractInput(transaction, txIn, vin) |
|
|
|
}) |
|
|
|
|
|
|
@ -298,7 +307,7 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { |
|
|
|
|
|
|
|
// Create script signatures from inputs
|
|
|
|
this.inputs.forEach(function (input, index) { |
|
|
|
var scriptType = input.scriptType |
|
|
|
var scriptType = input.redeemScriptType || input.prevOutType |
|
|
|
var scriptSig |
|
|
|
|
|
|
|
if (!allowIncomplete) { |
|
|
@ -310,46 +319,55 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { |
|
|
|
} |
|
|
|
|
|
|
|
if (input.signatures) { |
|
|
|
switch (scriptType) { |
|
|
|
case 'pubkeyhash': |
|
|
|
var pkhSignature = input.signatures[0].toScriptSignature(input.hashType) |
|
|
|
scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0]) |
|
|
|
break |
|
|
|
|
|
|
|
case 'multisig': |
|
|
|
var msSignatures = input.signatures.map(function (signature) { |
|
|
|
return signature && signature.toScriptSignature(input.hashType) |
|
|
|
}) |
|
|
|
|
|
|
|
// fill in blanks with OP_0
|
|
|
|
if (allowIncomplete) { |
|
|
|
for (var i = 0; i < msSignatures.length; ++i) { |
|
|
|
msSignatures[i] = msSignatures[i] || ops.OP_0 |
|
|
|
var processScript = function (scriptType, parentType, redeemScript) { |
|
|
|
var scriptSig |
|
|
|
var pkhSignature |
|
|
|
|
|
|
|
switch (scriptType) { |
|
|
|
case 'pubkeyhash': |
|
|
|
pkhSignature = input.signatures[0].toScriptSignature(input.hashType) |
|
|
|
scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0]) |
|
|
|
break |
|
|
|
|
|
|
|
case 'multisig': |
|
|
|
var msSignatures = input.signatures.map(function (signature) { |
|
|
|
return signature && signature.toScriptSignature(input.hashType) |
|
|
|
}) |
|
|
|
|
|
|
|
// fill in blanks with OP_0
|
|
|
|
if (allowIncomplete) { |
|
|
|
for (var i = 0; i < msSignatures.length; ++i) { |
|
|
|
msSignatures[i] = msSignatures[i] || ops.OP_0 |
|
|
|
} |
|
|
|
|
|
|
|
// remove blank signatures
|
|
|
|
} else { |
|
|
|
msSignatures = msSignatures.filter(function (x) { return x }) |
|
|
|
} |
|
|
|
|
|
|
|
// remove blank signatures
|
|
|
|
} else { |
|
|
|
msSignatures = msSignatures.filter(function (x) { return x }) |
|
|
|
} |
|
|
|
scriptSig = bscript.multisigInput(msSignatures, allowIncomplete ? undefined : redeemScript) |
|
|
|
|
|
|
|
var redeemScript = allowIncomplete ? undefined : input.redeemScript |
|
|
|
scriptSig = bscript.multisigInput(msSignatures, redeemScript) |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
case 'pubkey': |
|
|
|
var pkSignature = input.signatures[0].toScriptSignature(input.hashType) |
|
|
|
scriptSig = bscript.pubKeyInput(pkSignature) |
|
|
|
break |
|
|
|
case 'pubkey': |
|
|
|
var pkSignature = input.signatures[0].toScriptSignature(input.hashType) |
|
|
|
scriptSig = bscript.pubKeyInput(pkSignature) |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
// wrap as scriptHash if necessary
|
|
|
|
if (parentType === 'scripthash') { |
|
|
|
scriptSig = bscript.scriptHashInput(scriptSig, redeemScript) |
|
|
|
} |
|
|
|
|
|
|
|
return scriptSig |
|
|
|
} |
|
|
|
|
|
|
|
scriptSig = processScript(scriptType, input.prevOutType, input.redeemScript) |
|
|
|
} |
|
|
|
|
|
|
|
// did we build a scriptSig?
|
|
|
|
// did we build a scriptSig? Buffer('') is allowed
|
|
|
|
if (scriptSig) { |
|
|
|
// wrap as scriptHash if necessary
|
|
|
|
if (input.prevOutType === 'scripthash') { |
|
|
|
scriptSig = bscript.scriptHashInput(scriptSig, input.redeemScript) |
|
|
|
} |
|
|
|
|
|
|
|
tx.setInputScript(index, scriptSig) |
|
|
|
} |
|
|
|
}) |
|
|
@ -367,11 +385,12 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
input.prevOutScript && |
|
|
|
input.prevOutType && |
|
|
|
input.pubKeys && |
|
|
|
input.scriptType && |
|
|
|
input.redeemScriptType && |
|
|
|
input.signatures && |
|
|
|
input.signatures.length === input.pubKeys.length |
|
|
|
|
|
|
|
var kpPubKey = keyPair.getPublicKeyBuffer() |
|
|
|
var signatureScript |
|
|
|
|
|
|
|
// are we ready to sign?
|
|
|
|
if (canSign) { |
|
|
@ -385,6 +404,7 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
// no? prepare
|
|
|
|
} else { |
|
|
|
// must be pay-to-scriptHash?
|
|
|
|
|
|
|
|
if (redeemScript) { |
|
|
|
// if we have a prevOutScript, enforce scriptHash equality to the redeemScript
|
|
|
|
if (input.prevOutScript) { |
|
|
@ -394,34 +414,43 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
if (!bufferEquals(scriptHash, bcrypto.hash160(redeemScript))) throw new Error('RedeemScript does not match ' + scriptHash.toString('hex')) |
|
|
|
} |
|
|
|
|
|
|
|
var scriptType = bscript.classifyOutput(redeemScript) |
|
|
|
var redeemScriptChunks = bscript.decompile(redeemScript) |
|
|
|
var pubKeys |
|
|
|
var pubKeys, pkh1, pkh2 |
|
|
|
|
|
|
|
switch (scriptType) { |
|
|
|
case 'multisig': |
|
|
|
pubKeys = redeemScriptChunks.slice(1, -2) |
|
|
|
var redeemScriptType |
|
|
|
|
|
|
|
break |
|
|
|
var processScript = function (redeemScript) { |
|
|
|
var scriptType = bscript.classifyOutput(redeemScript) |
|
|
|
var redeemScriptChunks = bscript.decompile(redeemScript) |
|
|
|
|
|
|
|
case 'pubkeyhash': |
|
|
|
var pkh1 = redeemScriptChunks[2] |
|
|
|
var pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer()) |
|
|
|
switch (scriptType) { |
|
|
|
case 'multisig': |
|
|
|
pubKeys = redeemScriptChunks.slice(1, -2) |
|
|
|
|
|
|
|
if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input') |
|
|
|
pubKeys = [kpPubKey] |
|
|
|
break |
|
|
|
|
|
|
|
break |
|
|
|
case 'pubkeyhash': |
|
|
|
pkh1 = redeemScriptChunks[2] |
|
|
|
pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer()) |
|
|
|
|
|
|
|
case 'pubkey': |
|
|
|
pubKeys = redeemScriptChunks.slice(0, 1) |
|
|
|
if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input') |
|
|
|
pubKeys = [kpPubKey] |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
break |
|
|
|
case 'pubkey': |
|
|
|
pubKeys = redeemScriptChunks.slice(0, 1) |
|
|
|
|
|
|
|
default: |
|
|
|
throw new Error('RedeemScript not supported (' + scriptType + ')') |
|
|
|
break |
|
|
|
|
|
|
|
default: |
|
|
|
throw new Error('RedeemScript not supported (' + scriptType + ')') |
|
|
|
} |
|
|
|
|
|
|
|
return scriptType |
|
|
|
} |
|
|
|
|
|
|
|
redeemScriptType = processScript(redeemScript) |
|
|
|
|
|
|
|
// if we don't have a prevOutScript, generate a P2SH script
|
|
|
|
if (!input.prevOutScript) { |
|
|
|
input.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) |
|
|
@ -430,7 +459,7 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
|
|
|
|
input.pubKeys = pubKeys |
|
|
|
input.redeemScript = redeemScript |
|
|
|
input.scriptType = scriptType |
|
|
|
input.redeemScriptType = redeemScriptType |
|
|
|
input.signatures = pubKeys.map(function () { return undefined }) |
|
|
|
} else { |
|
|
|
// pay-to-scriptHash is not possible without a redeemScript
|
|
|
@ -440,6 +469,7 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
if (!input.scriptType) { |
|
|
|
input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(keyPair.getPublicKeyBuffer())) |
|
|
|
input.prevOutType = 'pubkeyhash' |
|
|
|
|
|
|
|
input.pubKeys = [kpPubKey] |
|
|
|
input.scriptType = input.prevOutType |
|
|
|
input.signatures = [undefined] |
|
|
@ -453,7 +483,7 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
} |
|
|
|
|
|
|
|
// ready to sign?
|
|
|
|
var signatureScript = input.redeemScript || input.prevOutScript |
|
|
|
signatureScript = signatureScript || input.redeemScript || input.prevOutScript |
|
|
|
var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) |
|
|
|
|
|
|
|
// enforce in order signing of public keys
|
|
|
@ -461,8 +491,7 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash |
|
|
|
if (!bufferEquals(kpPubKey, pubKey)) return false |
|
|
|
if (input.signatures[i]) throw new Error('Signature already exists') |
|
|
|
|
|
|
|
var signature = keyPair.sign(signatureHash) |
|
|
|
input.signatures[i] = signature |
|
|
|
input.signatures[i] = keyPair.sign(signatureHash) |
|
|
|
|
|
|
|
return true |
|
|
|
}) |
|
|
|