Merge pull request #47 from Christewart/refactor_flag_checks_to_sig_checker

Refactor flag checks to sig checker
This commit is contained in:
Chris Stewart 2016-12-29 11:17:52 -06:00 committed by GitHub
commit c5aabd39f7
9 changed files with 83 additions and 92 deletions

View file

@ -68,9 +68,10 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
TransactionSignatureSerializer.hashForSignature(w.transaction,w.inputIndex,sigsRemovedScript, hashType, w.amount,w.sigVersion)
}
logger.info("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature.bytes))
logger.debug("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature.bytes))
val isValid = pubKey.verify(hashForSignature,signature)
if (isValid) SignatureValidationSuccess else SignatureValidationFailureIncorrectSignatures
if (isValid) SignatureValidationSuccess
else nullFailCheck(Seq(signature),SignatureValidationFailureIncorrectSignatures, flags)
}
}
@ -94,15 +95,15 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
if (sigs.size > pubKeys.size) {
//this is how bitcoin core treats this. If there are ever any more
//signatures than public keys remaining we immediately return
//false https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L955-L959
//false https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L943-L945
logger.warn("We have more sigs than we have public keys remaining")
SignatureValidationFailureIncorrectSignatures
nullFailCheck(sigs,SignatureValidationFailureIncorrectSignatures,flags)
}
else if (requiredSigs > sigs.size) {
//for the case when we do not have enough sigs left to check to meet the required signature threshold
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915
//https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L990-L991
logger.warn("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
SignatureValidationFailureSignatureCount
nullFailCheck(sigs,SignatureValidationFailureSignatureCount,flags)
}
else if (sigs.nonEmpty && pubKeys.nonEmpty) {
val sig = sigs.head
@ -111,18 +112,34 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
result match {
case SignatureValidationSuccess =>
multiSignatureEvaluator(txSignatureComponent, script, sigs.tail,pubKeys.tail,flags, requiredSigs - 1)
case SignatureValidationFailureIncorrectSignatures =>
case SignatureValidationFailureIncorrectSignatures | SignatureValidationErrorNullFail =>
//notice we pattern match on 'SignatureValidationErrorNullFail' here, this is because
//'checkSignature' may return that result, but we need to continue evaluating the signatures
//in the multisig script, we don't check for nullfail until evaluation the OP_CHECKMULTSIG is completely done
multiSignatureEvaluator(txSignatureComponent, script, sigs, pubKeys.tail,flags, requiredSigs)
case x @ (SignatureValidationFailureNotStrictDerEncoding | SignatureValidationFailureSignatureCount |
SignatureValidationFailurePubKeyEncoding | ScriptValidationFailureHighSValue |
ScriptValidationFailureHashType | ScriptValidationFailureWitnessPubKeyType) =>
x
nullFailCheck(sigs,x,flags)
}
} else if (sigs.isEmpty) {
//means that we have checked all of the sigs against the public keys
//validation succeeds
SignatureValidationSuccess
} else SignatureValidationFailureIncorrectSignatures
} else nullFailCheck(sigs,SignatureValidationFailureIncorrectSignatures,flags)
}
/** If the NULLFAIL flag is set as defined in BIP146, it checks to make sure all failed signatures were an empty byte vector
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL]]
* */
private def nullFailCheck(sigs: Seq[ECDigitalSignature], result: TransactionSignatureCheckerResult,flags: Seq[ScriptFlag]): TransactionSignatureCheckerResult = {
logger.info("Result before nullfail check:" + result)
val nullFailEnabled = ScriptFlagUtil.requireScriptVerifyNullFail(flags)
if (nullFailEnabled && !result.isValid) {
//we need to check that all signatures were empty byte vectors, else this fails because of BIP146 and nullfail
if (sigs.exists(_.bytes.nonEmpty)) SignatureValidationErrorNullFail else result
} else result
}
}

View file

@ -64,3 +64,10 @@ case object ScriptValidationFailureHashType extends SignatureValidationError
/** Fails the script if the given public key was not compressed and the [[org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType]]
* flag was set */
case object ScriptValidationFailureWitnessPubKeyType extends SignatureValidationError
/**
* Fails the script if a an invalid signature is not an empty byte vector
* See BIP146
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#nullfail]]
*/
case object SignatureValidationErrorNullFail extends SignatureValidationError

View file

@ -2,7 +2,6 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
@ -16,7 +15,7 @@ sealed trait ScriptWitness extends NetworkElement {
/** The byte vectors that are placed on to the stack when evaluating a witness program */
def stack : Seq[Seq[Byte]]
override def toString = stack.map(BitcoinSUtil.encodeHex(_)).toString
override def toString = "ScriptWitness(" + stack.map(BitcoinSUtil.encodeHex(_)).toString + ")"
override def hex = RawScriptWitnessParser.write(this)
}

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.serializers.transaction.RawTransactionInputParser
import org.bitcoins.core.util.Factory

View file

@ -3,7 +3,7 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.serializers.transaction.RawTransactionWitnessParser
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
import org.bitcoins.core.util.BitcoinSUtil
/**
* Created by chris on 11/21/16.

View file

@ -67,60 +67,40 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
val pubKey = ECPublicKey(executionInProgressScriptProgram.stack.head.bytes)
val signature = ECDigitalSignature(executionInProgressScriptProgram.stack.tail.head.bytes)
val flags = executionInProgressScriptProgram.flags
if (ScriptFlagUtil.requiresStrictDerEncoding(flags) &&
!DERSignatureUtil.isValidSignatureEncoding(signature)) {
//this means all of the signatures must encoded according to BIP66 strict dersig
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
//script verification fails since the sig is not strictly der encoded
logger.error("Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
"Sig: " + signature.hex)
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigDer)
} else if (BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags).isDefined) {
logger.error("Pubkey had an invalid encoding")
val err = BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags).get
logger.error("PubKey error: " + err)
ScriptProgram(executionInProgressScriptProgram,err)
} else {
val restOfStack = executionInProgressScriptProgram.stack.tail.tail
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, flags)
logger.debug("signature verification isValid: " + result)
val restOfStack = executionInProgressScriptProgram.stack.tail.tail
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, flags)
logger.debug("signature verification isValid: " + result)
result match {
case SignatureValidationSuccess => ScriptProgram(program,
OP_TRUE :: restOfStack, program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailureNotStrictDerEncoding)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
logger.info("Signature validation failed: " + SignatureValidationFailureIncorrectSignatures)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
logger.info("Signature validation failed: " + SignatureValidationFailureSignatureCount)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailurePubKeyEncoding)
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
logger.info("Signature validation failed: " + ScriptValidationFailureHighSValue)
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
logger.info("Signature validation failed: " + ScriptValidationFailureHashType)
ScriptProgram(program,ScriptErrorSigHashType)
case ScriptValidationFailureWitnessPubKeyType =>
ScriptProgram(program,ScriptErrorWitnessPubKeyType)
case SignatureValidationErrorNullFail =>
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullFail)
result match {
case SignatureValidationSuccess => ScriptProgram(program,
OP_TRUE :: restOfStack, program.script.tail)
case err : SignatureValidationError =>
if (ScriptFlagUtil.requireScriptVerifyNullFail(flags) && signature.bytes.nonEmpty) {
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigNullFail)
} else {
err match {
case SignatureValidationFailureNotStrictDerEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailureNotStrictDerEncoding)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
logger.info("Signature validation failed: " + SignatureValidationFailureIncorrectSignatures)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
logger.info("Signature validation failed: " + SignatureValidationFailureSignatureCount)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailurePubKeyEncoding)
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
logger.info("Signature validation failed: " + ScriptValidationFailureHighSValue)
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
logger.info("Signature validation failed: " + ScriptValidationFailureHashType)
ScriptProgram(program,ScriptErrorSigHashType)
case ScriptValidationFailureWitnessPubKeyType =>
ScriptProgram(program,ScriptErrorWitnessPubKeyType)
}
}
}
}
}
}
@ -252,7 +232,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
removedOpCodeSeparatorsScript, signatures,
pubKeys, flags, mRequiredSignatures.underlying)
//remove the extra op for OP_CHECKMULTISIG from the stack
//remove the extra OP_0 (null dummy) for OP_CHECKMULTISIG from the stack
val restOfStack = stackWithoutPubKeysAndSignatures.tail
isValidSignatures match {
@ -268,14 +248,9 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
val nullFailEnabled = ScriptFlagUtil.requireScriptVerifyNullFail(flags)
if (nullFailEnabled && signatures.exists(_.bytes.nonEmpty)) {
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullFail) }
else {
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(executionInProgressScriptProgram, OP_FALSE :: restOfStack, program.script.tail)
}
//this means that signature verification failed, however all signatures were encoded correctly
//just push a OP_FALSE onto the stack
ScriptProgram(executionInProgressScriptProgram, OP_FALSE :: restOfStack, program.script.tail)
case SignatureValidationFailureSignatureCount =>
//means that we did not have enough signatures for OP_CHECKMULTISIG
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
@ -288,6 +263,9 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigHashType)
case ScriptValidationFailureWitnessPubKeyType =>
ScriptProgram(executionInProgressScriptProgram,ScriptErrorWitnessPubKeyType)
case SignatureValidationErrorNullFail =>
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullFail)
}
}
}

View file

@ -1,6 +1,5 @@
package org.bitcoins.core.script.interpreter
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.crypto.{BaseTransactionSignatureComponent, WitnessV0TransactionSignatureComponent}
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
@ -10,7 +9,7 @@ import org.bitcoins.core.protocol.transaction.{BaseTransaction, EmptyTransaction
import org.bitcoins.core.script._
import org.bitcoins.core.script.arithmetic._
import org.bitcoins.core.script.bitwise._
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.constant.{ScriptToken, _}
import org.bitcoins.core.script.control._
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag._
@ -75,8 +74,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case p2sh : P2SHScriptPubKey =>
if (p2shEnabled) executeP2shScript(scriptSigExecutedProgram, program, p2sh)
else scriptPubKeyExecutedProgram
case _ @ (_ : P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey |
_ : CLTVScriptPubKey | _ : NonStandardScriptPubKey | EmptyScriptPubKey) =>
case _ : P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey |
_ : CLTVScriptPubKey | _ : NonStandardScriptPubKey | _ : WitnessCommitment | EmptyScriptPubKey =>
scriptPubKeyExecutedProgram
}
}
@ -160,7 +159,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
run(scriptPubKeyExecutedProgram,stack,w)
}
case s @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | EmptyScriptPubKey) =>
_ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | _ : WitnessCommitment | EmptyScriptPubKey) =>
logger.debug("redeemScript: " + s.asm)
run(scriptPubKeyExecutedProgram,stack,s)
}
@ -516,7 +515,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
w.witness.stack.isEmpty
}
case _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _ : MultiSignatureScriptPubKey | _ : NonStandardScriptPubKey |
_ : P2PKScriptPubKey | _ : P2PKHScriptPubKey | EmptyScriptPubKey =>
_ : P2PKScriptPubKey | _ : P2PKHScriptPubKey | _ : WitnessCommitment | EmptyScriptPubKey =>
w.witness.stack.isEmpty
}
!witnessedUsed

View file

@ -38,6 +38,7 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
logger.info("result: " + result)
result == ScriptOk
}

View file

@ -31,17 +31,7 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
//use this to represent a single test case from script_valid.json
/* val lines =
"""
| [ [
| [
| "",
| 0.00000000
| ],
| "0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001",
| "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG",
| "P2SH,WITNESS",
| "WITNESS_UNEXPECTED",
| "P2PK with witness"
| ]]
| [ ["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"]]
""".stripMargin*/
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson