move & rename anything to do with signable_scripts
This commit is contained in:
parent
b09cf89f97
commit
3a03691b7d
@ -167,16 +167,91 @@ function InSigner (tx, nIn, opts) {
|
|||||||
this.nIn = nIn
|
this.nIn = nIn
|
||||||
this.publicKeys = []
|
this.publicKeys = []
|
||||||
this.signatures = []
|
this.signatures = []
|
||||||
this.requiredSigs = null
|
this.requiredSigs = 0
|
||||||
this.solve(opts)
|
this.solve(opts)
|
||||||
this.extractSig()
|
this.extractSig()
|
||||||
}
|
}
|
||||||
|
|
||||||
InSigner.prototype.isFullySigned = function () {
|
InSigner.prototype.solve = function (opts) {
|
||||||
return this.requiredSigs !== null && this.requiredSigs === this.signatures.length
|
// Determine scriptPubKey is solvable
|
||||||
|
var scriptPubKey = solveScript(opts.scriptPubKey)
|
||||||
|
if (scriptPubKey.type !== bscript.types.P2SH && ALLOWED_P2SH_SCRIPTS.indexOf(scriptPubKey.type) === -1) {
|
||||||
|
throw new Error('txOut script is non-standard')
|
||||||
|
}
|
||||||
|
|
||||||
|
var solution = scriptPubKey
|
||||||
|
var redeemScript
|
||||||
|
var witnessScript
|
||||||
|
var sigVersion = Transaction.SIG_V0
|
||||||
|
var value = 0
|
||||||
|
|
||||||
|
if (solution.type === bscript.types.P2SH) {
|
||||||
|
var scriptHash = solution.solvedBy
|
||||||
|
// Redeem script must be provided by opts
|
||||||
|
if (!(opts.redeemScript instanceof Buffer)) {
|
||||||
|
throw new Error('Redeem script required to solve utxo')
|
||||||
|
}
|
||||||
|
// Check redeemScript against script-hash
|
||||||
|
if (!scriptHash.equals(bcrypto.hash160(opts.redeemScript))) {
|
||||||
|
throw new Error('Redeem script does not match txOut script hash')
|
||||||
|
}
|
||||||
|
// Prevent non-standard P2SH scripts
|
||||||
|
solution = solveScript(opts.redeemScript)
|
||||||
|
if (!ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) {
|
||||||
|
throw new Error('Unsupported P2SH script')
|
||||||
|
}
|
||||||
|
redeemScript = solution
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solution.type === bscript.types.P2WPKH) {
|
||||||
|
// txOutValue is required here
|
||||||
|
if (!types.Satoshi(opts.value)) {
|
||||||
|
throw new Error('Value is required for witness-key-hash')
|
||||||
|
}
|
||||||
|
// We set solution to this to avoid work later, since P2WPKH is a special case of P2PKH
|
||||||
|
solution = solveScript(bscript.pubKeyHash.output.encode(solution.solvedBy))
|
||||||
|
sigVersion = Transaction.SIG_V1
|
||||||
|
value = opts.value
|
||||||
|
} else if (solution.type === bscript.types.P2WSH) {
|
||||||
|
var witnessScriptHash = solution.solvedBy
|
||||||
|
// Witness script must be provided by opts
|
||||||
|
if (!(opts.witnessScript instanceof Buffer)) {
|
||||||
|
throw new Error('P2WSH script required to solve utxo')
|
||||||
|
}
|
||||||
|
// txOutValue is required here
|
||||||
|
if (!types.Satoshi(opts.value)) {
|
||||||
|
throw new Error('Value is required for witness-script-hash')
|
||||||
|
}
|
||||||
|
// Check witnessScript against script hash
|
||||||
|
if (!bcrypto.sha256(opts.witnessScript).equals(witnessScriptHash)) {
|
||||||
|
throw new Error('Witness script does not match txOut script hash')
|
||||||
|
}
|
||||||
|
// Prevent non-standard P2WSH scripts
|
||||||
|
solution = solveScript(opts.witnessScript)
|
||||||
|
if (SIGNABLE_SCRIPTS.indexOf(solution.type) === -1) {
|
||||||
|
throw new Error('Witness script is not supported')
|
||||||
|
}
|
||||||
|
witnessScript = solution
|
||||||
|
sigVersion = Transaction.SIG_V1
|
||||||
|
value = opts.value
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requiredSigs = solution.requiredSigs
|
||||||
|
this.scriptPubKey = scriptPubKey
|
||||||
|
this.redeemScript = redeemScript
|
||||||
|
this.witnessScript = witnessScript
|
||||||
|
this.value = value
|
||||||
|
// signScript & sigVersion were added last, they make sign branch free
|
||||||
|
this.sigVersion = sigVersion
|
||||||
|
this.signScript = solution
|
||||||
}
|
}
|
||||||
|
|
||||||
InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
|
InSigner.prototype.isFullySigned = function () {
|
||||||
|
return this.requiredSigs !== null && this.signatures.length === this.requiredSigs
|
||||||
|
}
|
||||||
|
|
||||||
|
InSigner.prototype.extractSignableChunks = function (solution, chunks, sigVersion) {
|
||||||
|
// Only SIGNABLE_SCRIPTS can be extracted here
|
||||||
var signatures = []
|
var signatures = []
|
||||||
var publicKeys = []
|
var publicKeys = []
|
||||||
var decoded
|
var decoded
|
||||||
@ -222,84 +297,12 @@ InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
|
|||||||
return [signatures, publicKeys]
|
return [signatures, publicKeys]
|
||||||
}
|
}
|
||||||
|
|
||||||
InSigner.prototype.solve = function (opts) {
|
|
||||||
// Determine scriptPubKey is solvable
|
|
||||||
var scriptPubKey = solveScript(opts.scriptPubKey)
|
|
||||||
if (scriptPubKey.type !== bscript.types.P2SH && ALLOWED_P2SH_SCRIPTS.indexOf(scriptPubKey.type) === -1) {
|
|
||||||
throw new Error('txOut script is non-standard')
|
|
||||||
}
|
|
||||||
|
|
||||||
var solution = scriptPubKey
|
|
||||||
var redeemScript
|
|
||||||
var witnessScript
|
|
||||||
var sigVersion = Transaction.SIG_V0
|
|
||||||
var value = 0
|
|
||||||
|
|
||||||
if (solution.type === bscript.types.P2SH) {
|
|
||||||
var scriptHash = solution.solvedBy
|
|
||||||
// Redeem script must be provided by opts
|
|
||||||
if (!(opts.redeemScript instanceof Buffer)) {
|
|
||||||
throw new Error('Redeem script required to solve utxo')
|
|
||||||
}
|
|
||||||
// Check redeemScript against script-hash
|
|
||||||
if (!scriptHash.equals(bcrypto.hash160(opts.redeemScript))) {
|
|
||||||
throw new Error('Redeem script does not match txOut script hash')
|
|
||||||
}
|
|
||||||
// Determine redeemScript is solvable
|
|
||||||
solution = solveScript(opts.redeemScript)
|
|
||||||
if (!ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) {
|
|
||||||
throw new Error('Unsupported P2SH script')
|
|
||||||
}
|
|
||||||
redeemScript = solution
|
|
||||||
}
|
|
||||||
|
|
||||||
if (solution.type === bscript.types.P2WPKH) {
|
|
||||||
// txOutValue is required here
|
|
||||||
if (!types.Satoshi(opts.value)) {
|
|
||||||
throw new Error('Value is required for witness-key-hash')
|
|
||||||
}
|
|
||||||
// We set solution to this to avoid work later, since P2WPKH is a special case of P2PKH
|
|
||||||
solution = solveScript(bscript.pubKeyHash.output.encode(solution.solvedBy))
|
|
||||||
sigVersion = Transaction.SIG_V1
|
|
||||||
value = opts.value
|
|
||||||
} else if (solution.type === bscript.types.P2WSH) {
|
|
||||||
var witnessScriptHash = solution.solvedBy
|
|
||||||
// Witness script must be provided by opts
|
|
||||||
if (!(opts.witnessScript instanceof Buffer)) {
|
|
||||||
throw new Error('P2WSH script required to solve utxo')
|
|
||||||
}
|
|
||||||
// txOutValue is required here
|
|
||||||
if (!types.Satoshi(opts.value)) {
|
|
||||||
throw new Error('Value is required for witness-script-hash')
|
|
||||||
}
|
|
||||||
// Check witnessScript against script hash
|
|
||||||
if (!bcrypto.sha256(opts.witnessScript).equals(witnessScriptHash)) {
|
|
||||||
throw new Error('Witness script does not match txOut script hash')
|
|
||||||
}
|
|
||||||
solution = solveScript(opts.witnessScript)
|
|
||||||
// Determine if witnessScript is solvable
|
|
||||||
if (SIGNABLE_SCRIPTS.indexOf(solution.type) === -1) {
|
|
||||||
throw new Error('witness script is not supported')
|
|
||||||
}
|
|
||||||
witnessScript = solution
|
|
||||||
sigVersion = Transaction.SIG_V1
|
|
||||||
value = opts.value
|
|
||||||
}
|
|
||||||
|
|
||||||
this.signScript = solution
|
|
||||||
this.scriptPubKey = scriptPubKey
|
|
||||||
this.redeemScript = redeemScript
|
|
||||||
this.witnessScript = witnessScript
|
|
||||||
this.sigVersion = sigVersion
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
InSigner.prototype.extractSig = function () {
|
InSigner.prototype.extractSig = function () {
|
||||||
// Attempt decoding of the input scriptSig and witness
|
// Attempt decoding of the input scriptSig and witness
|
||||||
var input = this.tx.ins[this.nIn]
|
var input = this.tx.ins[this.nIn]
|
||||||
var solution = this.scriptPubKey
|
var solution = this.scriptPubKey
|
||||||
var extractChunks = []
|
var extractChunks = []
|
||||||
if (solution.canSign) {
|
if (SIGNABLE_SCRIPTS.indexOf(solution.type) !== -1) {
|
||||||
extractChunks = evalPushOnly(input.script)
|
extractChunks = evalPushOnly(input.script)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,8 +314,8 @@ InSigner.prototype.extractSig = function () {
|
|||||||
throw new Error('Redeem script from scriptSig does not match')
|
throw new Error('Redeem script from scriptSig does not match')
|
||||||
}
|
}
|
||||||
solution = this.redeemScript
|
solution = this.redeemScript
|
||||||
if (solution.canSign) {
|
if (SIGNABLE_SCRIPTS.indexOf(solution.canSign) !== -1) {
|
||||||
// We only bother if canSign, otherwise we waste time
|
// only do this if we can sign, otherwise we waste CPU
|
||||||
extractChunks = evalPushOnly(p2sh.redeemScriptSig)
|
extractChunks = evalPushOnly(p2sh.redeemScriptSig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,42 +341,44 @@ InSigner.prototype.extractSig = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (extractChunks.length > 0) {
|
if (extractChunks.length > 0) {
|
||||||
[this.signatures, this.publicKeys] = this.extractStandard(solution, extractChunks, this.sigVersion)
|
[this.signatures, this.publicKeys] = this.extractSignableChunks(solution, extractChunks, this.sigVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function signStandard (tx, nIn, txOutValue, signatures, publicKeys, key, solution, sigHashType, sigVersion) {
|
function signSignable (tx, nIn, txOutValue, signatures, publicKeys, key, solution, sigHashType, sigVersion) {
|
||||||
|
// Todo, skip if already signed
|
||||||
// Only SIGNABLE_SCRIPTS can be signed here
|
// Only SIGNABLE_SCRIPTS can be signed here
|
||||||
var didSign = false
|
|
||||||
var keyBuffer = key.getPublicKeyBuffer()
|
var keyBuffer = key.getPublicKeyBuffer()
|
||||||
if (solution.type === bscript.types.P2PK) {
|
if (solution.type === bscript.types.P2PK) {
|
||||||
if (bufferEquals(keyBuffer, solution.solvedBy)) {
|
if (bufferEquals(keyBuffer, solution.solvedBy)) {
|
||||||
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
|
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
|
||||||
publicKeys = [keyBuffer]
|
publicKeys = [keyBuffer]
|
||||||
didSign = true
|
} else {
|
||||||
|
throw new Error('Signing input with wrong private key')
|
||||||
}
|
}
|
||||||
} else if (solution.type === bscript.types.P2PKH) {
|
} else if (solution.type === bscript.types.P2PKH) {
|
||||||
if (bufferEquals(bcrypto.hash160(keyBuffer), solution.solvedBy)) {
|
if (bufferEquals(bcrypto.hash160(keyBuffer), solution.solvedBy)) {
|
||||||
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
|
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
|
||||||
publicKeys = [keyBuffer]
|
publicKeys = [keyBuffer]
|
||||||
didSign = true
|
} else {
|
||||||
|
throw new Error('Signing input with wrong private key')
|
||||||
}
|
}
|
||||||
} else if (solution.type === bscript.types.MULTISIG) {
|
} else if (solution.type === bscript.types.MULTISIG) {
|
||||||
|
var match = false
|
||||||
for (var i = 0, keyLen = solution.solvedBy.pubKeys.length; i < keyLen; i++) {
|
for (var i = 0, keyLen = solution.solvedBy.pubKeys.length; i < keyLen; i++) {
|
||||||
publicKeys[i] = solution.solvedBy.pubKeys[i]
|
publicKeys[i] = solution.solvedBy.pubKeys[i]
|
||||||
if (bufferEquals(keyBuffer, publicKeys[i])) {
|
if (bufferEquals(keyBuffer, publicKeys[i])) {
|
||||||
didSign = true
|
match = true
|
||||||
signatures[i] = calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)
|
signatures[i] = calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!match) {
|
||||||
|
throw new Error('Signing input with wrong private key')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('signStandard can only sign SIGNABLE_SCRIPTS')
|
throw new Error('signStandard can only sign SIGNABLE_SCRIPTS')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!didSign) {
|
|
||||||
throw new Error('Signing input with wrong private key')
|
|
||||||
}
|
|
||||||
|
|
||||||
return [signatures, publicKeys]
|
return [signatures, publicKeys]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,14 +389,11 @@ function signStandard (tx, nIn, txOutValue, signatures, publicKeys, key, solutio
|
|||||||
* @returns {{type, script, canSign, solvedBy, requiredSigs}|*}
|
* @returns {{type, script, canSign, solvedBy, requiredSigs}|*}
|
||||||
*/
|
*/
|
||||||
InSigner.prototype.sign = function (key, sigHashType) {
|
InSigner.prototype.sign = function (key, sigHashType) {
|
||||||
sigHashType = sigHashType || Transaction.SIGHASH_ALL
|
sigHashType = sigHashType || Transaction.SIGHASH_ALL;
|
||||||
var solution = this.signScript;
|
[this.signatures, this.publicKeys] = signSignable(this.tx, this.nIn, this.value, this.signatures, this.publicKeys, key, this.signScript, sigHashType, this.sigVersion)
|
||||||
// Attempt to solve the txOut script
|
|
||||||
|
|
||||||
[this.signatures, this.publicKeys] = signStandard(this.tx, this.nIn, this.value, this.signatures, this.publicKeys, key, solution, sigHashType, this.sigVersion)
|
|
||||||
this.requiredSigs = this.publicKeys.length
|
this.requiredSigs = this.publicKeys.length
|
||||||
|
|
||||||
return solution
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -403,8 +405,8 @@ InSigner.prototype.sign = function (key, sigHashType) {
|
|||||||
* @param publicKeys
|
* @param publicKeys
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
function serializeStandard (outputType, signatures, publicKeys) {
|
function serializeSignableChunks (outputType, signatures, publicKeys) {
|
||||||
// When adding a new script type, edit here
|
// Only SIGNABLE_SCRIPTS can be serialized here
|
||||||
var chunks = []
|
var chunks = []
|
||||||
switch (outputType) {
|
switch (outputType) {
|
||||||
case bscript.types.P2PK:
|
case bscript.types.P2PK:
|
||||||
@ -438,7 +440,7 @@ InSigner.prototype.serializeSigData = function () {
|
|||||||
var witnessChunks = []
|
var witnessChunks = []
|
||||||
var type = this.scriptPubKey.type
|
var type = this.scriptPubKey.type
|
||||||
if (this.scriptPubKey.canSign) {
|
if (this.scriptPubKey.canSign) {
|
||||||
scriptChunks = serializeStandard(type, this.signatures, this.publicKeys)
|
scriptChunks = serializeSignableChunks(type, this.signatures, this.publicKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p2sh = false
|
var p2sh = false
|
||||||
@ -446,17 +448,17 @@ InSigner.prototype.serializeSigData = function () {
|
|||||||
p2sh = true
|
p2sh = true
|
||||||
type = this.redeemScript.type
|
type = this.redeemScript.type
|
||||||
if (this.redeemScript.canSign) {
|
if (this.redeemScript.canSign) {
|
||||||
scriptChunks = serializeStandard(type, this.signatures, this.publicKeys)
|
scriptChunks = serializeSignableChunks(type, this.signatures, this.publicKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === bscript.types.P2WPKH) {
|
if (type === bscript.types.P2WPKH) {
|
||||||
witnessChunks = serializeStandard(bscript.types.P2PKH, this.signatures, this.publicKeys)
|
witnessChunks = serializeSignableChunks(bscript.types.P2PKH, this.signatures, this.publicKeys)
|
||||||
} else if (type === bscript.types.P2WSH) {
|
} else if (type === bscript.types.P2WSH) {
|
||||||
type = this.witnessScript.type
|
type = this.witnessScript.type
|
||||||
if (this.witnessScript.canSign) {
|
if (this.witnessScript.canSign) {
|
||||||
scriptChunks = []
|
scriptChunks = []
|
||||||
witnessChunks = serializeStandard(type, this.signatures, this.publicKeys)
|
witnessChunks = serializeSignableChunks(type, this.signatures, this.publicKeys)
|
||||||
witnessChunks.push(this.witnessScript.script);
|
witnessChunks.push(this.witnessScript.script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user