diff --git a/src/main/scala/org/bitcoins/core/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/bitcoins/core/script/interpreter/ScriptInterpreter.scala index af45af5a0e..fd841e1805 100644 --- a/src/main/scala/org/bitcoins/core/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/bitcoins/core/script/interpreter/ScriptInterpreter.scala @@ -35,15 +35,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con */ private lazy val maxScriptOps = 201 - //TODO: Think about this more to change this from being mutable to immutable - perhaps - //passing this as an implicit argument to our loop function - /** - * The current operation count that a script has performed - * This is limited by bitcoin core to be 201 operations - * - */ - private var opCount = 0 - /** * Runs an entire script though our script programming language and * returns true or false depending on if the script was valid @@ -65,7 +56,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con logger.error("P2SH scriptSigs are required to be push only by definition - see BIP16") ScriptProgram(programBeingExecuted,ScriptErrorSigPushOnly) } else { - val scriptSigExecutedProgram = loop(program) + val scriptSigExecutedProgram = loop(program,0) scriptPubKey match { case p2shScriptPubKey : P2SHScriptPubKey if (ScriptFlagUtil.p2shEnabled(program.flags)) => executeP2shScript(scriptSigExecutedProgram, programBeingExecuted, p2shScriptPubKey) @@ -78,7 +69,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent, scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm) require(scriptPubKeyProgram.script == scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm) - val scriptPubKeyExecutedProgram : ExecutedScriptProgram = loop(scriptPubKeyProgram) + val scriptPubKeyExecutedProgram : ExecutedScriptProgram = loop(scriptPubKeyProgram,0) logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack) @@ -121,7 +112,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con scriptSigExecutedProgram } else { val hashCheckProgram = ScriptProgram(originalProgram, scriptSigExecutedProgram.stack, p2shScriptPubKey.asm) - val hashesMatchProgram = loop(hashCheckProgram) + val hashesMatchProgram = loop(hashCheckProgram,0) hashesMatchProgram.stackTopIsTrue match { case true => logger.info("Hashes matched between the p2shScriptSignature & the p2shScriptPubKey") @@ -135,7 +126,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con if (ScriptFlagUtil.requirePushOnly(p2shRedeemScriptProgram.flags) && !BitcoinScriptUtil.isPushOnly(redeemScript.asm)) { logger.error("p2sh redeem script must be push only operations whe SIGPUSHONLY flag is set") ScriptProgram(p2shRedeemScriptProgram,ScriptErrorSigPushOnly) - } else loop(p2shRedeemScriptProgram) + } else loop(p2shRedeemScriptProgram,0) case false => logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript") hashesMatchProgram @@ -149,221 +140,212 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con * @return program the final state of the program after being evaluated by the interpreter */ @tailrec - private def loop(program : ScriptProgram) : ExecutedScriptProgram = { + private def loop(program : ScriptProgram, opCount: Int) : ExecutedScriptProgram = { logger.debug("Stack: " + program.stack) logger.debug("Script: " + program.script) if (opCount > maxScriptOps && !program.isInstanceOf[ExecutedScriptProgram]) { logger.error("We have reached the maximum amount of script operations allowed") logger.error("Here are the remaining operations in the script: " + program.script) - loop(ScriptProgram(program,ScriptErrorOpCount)) + loop(ScriptProgram(program,ScriptErrorOpCount),opCount) } else if (program.script.flatMap(_.bytes).size > 10000 && !program.isInstanceOf[ExecutedScriptProgram]) { logger.error("We cannot run a script that is larger than 10,000 bytes") program match { case p : PreExecutionScriptProgram => - loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize)) + loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize),opCount) case _ : ExecutionInProgressScriptProgram | _ : ExecutedScriptProgram => - loop(ScriptProgram(program, ScriptErrorScriptSize)) + loop(ScriptProgram(program, ScriptErrorScriptSize),opCount) } } else { program match { - case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack))) + case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)),opCount) case p : ExecutedScriptProgram => //reset opCount variable to zero since we may need to count the ops //in the scriptPubKey - we don't want the op count of the scriptSig //to count towards the scriptPubKey op count logger.info("Final op count: " + opCount) - opCount = 0 p case p : ExecutionInProgressScriptProgram => //increment the op count - if (p.script.headOption.isDefined && - BitcoinScriptUtil.countsTowardsScriptOpLimit(p.script.head)) opCount = opCount + 1 +/* if (p.script.headOption.isDefined && + BitcoinScriptUtil.countsTowardsScriptOpLimit(p.script.head)) opCount = opCount + 1*/ p.script match { //if at any time we see that the program is not valid //cease script execution case _ if !p.script.intersect(Seq(OP_VERIF, OP_VERNOTIF)).isEmpty => logger.error("Script is invalid even when a OP_VERIF or OP_VERNOTIF occurs in an unexecuted OP_IF branch") - loop(ScriptProgram(p, ScriptErrorBadOpCode)) + loop(ScriptProgram(p, ScriptErrorBadOpCode),opCount) //disabled splice operation case _ if !p.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).isEmpty => logger.error("Script is invalid because it contains a disabled splice operation") - loop(ScriptProgram(p, ScriptErrorDisabledOpCode)) + loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount) //disabled bitwise operations case _ if !p.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty => logger.error("Script is invalid because it contains a disabled bitwise operation") - loop(ScriptProgram(p, ScriptErrorDisabledOpCode)) + loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount) //disabled arithmetic operations case _ if !p.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).isEmpty => logger.error("Script is invalid because it contains a disabled arithmetic operation") - loop(ScriptProgram(p, ScriptErrorDisabledOpCode)) + loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount) //program cannot contain a push operation > 520 bytes case _ if (p.script.exists(token => token.bytes.size > 520)) => logger.error("We have a script constant that is larger than 520 bytes, this is illegal: " + p.script) - loop(ScriptProgram(p, ScriptErrorPushSize)) + loop(ScriptProgram(p, ScriptErrorPushSize),opCount) //program stack size cannot be greater than 1000 elements case _ if ((p.stack.size + p.altStack.size) > 1000) => logger.error("We cannot have a stack + alt stack size larger than 1000 elements") - loop(ScriptProgram(p, ScriptErrorStackSize)) + loop(ScriptProgram(p, ScriptErrorStackSize),opCount) //stack operations - case OP_DUP :: t => loop(opDup(p)) - case OP_DEPTH :: t => loop(opDepth(p)) - case OP_TOALTSTACK :: t => loop(opToAltStack(p)) - case OP_FROMALTSTACK :: t => loop(opFromAltStack(p)) - case OP_DROP :: t => loop(opDrop(p)) - case OP_IFDUP :: t => loop(opIfDup(p)) - case OP_NIP :: t => loop(opNip(p)) - case OP_OVER :: t => loop(opOver(p)) - case OP_PICK :: t => loop(opPick(p)) - case OP_ROLL :: t => loop(opRoll(p)) - case OP_ROT :: t => loop(opRot(p)) - case OP_2ROT :: t => loop(op2Rot(p)) - case OP_2DROP :: t => loop(op2Drop(p)) - case OP_SWAP :: t => loop(opSwap(p)) - case OP_TUCK :: t => loop(opTuck(p)) - case OP_2DUP :: t => loop(op2Dup(p)) - case OP_3DUP :: t => loop(op3Dup(p)) - case OP_2OVER :: t => loop(op2Over(p)) - case OP_2SWAP :: t => loop(op2Swap(p)) + case OP_DUP :: t => loop(opDup(p),calcOpCount(opCount,OP_DUP)) + case OP_DEPTH :: t => loop(opDepth(p),calcOpCount(opCount,OP_DEPTH)) + case OP_TOALTSTACK :: t => loop(opToAltStack(p),calcOpCount(opCount,OP_TOALTSTACK)) + case OP_FROMALTSTACK :: t => loop(opFromAltStack(p),calcOpCount(opCount,OP_FROMALTSTACK)) + case OP_DROP :: t => loop(opDrop(p),calcOpCount(opCount,OP_DROP)) + case OP_IFDUP :: t => loop(opIfDup(p),calcOpCount(opCount,OP_IFDUP)) + case OP_NIP :: t => loop(opNip(p),calcOpCount(opCount,OP_NIP)) + case OP_OVER :: t => loop(opOver(p),calcOpCount(opCount,OP_OVER)) + case OP_PICK :: t => loop(opPick(p),calcOpCount(opCount,OP_PICK)) + case OP_ROLL :: t => loop(opRoll(p),calcOpCount(opCount,OP_ROLL)) + case OP_ROT :: t => loop(opRot(p),calcOpCount(opCount,OP_ROT)) + case OP_2ROT :: t => loop(op2Rot(p),calcOpCount(opCount,OP_2ROT)) + case OP_2DROP :: t => loop(op2Drop(p),calcOpCount(opCount,OP_2DROP)) + case OP_SWAP :: t => loop(opSwap(p),calcOpCount(opCount,OP_SWAP)) + case OP_TUCK :: t => loop(opTuck(p),calcOpCount(opCount,OP_TUCK)) + case OP_2DUP :: t => loop(op2Dup(p),calcOpCount(opCount,OP_2DUP)) + case OP_3DUP :: t => loop(op3Dup(p),calcOpCount(opCount,OP_3DUP)) + case OP_2OVER :: t => loop(op2Over(p),calcOpCount(opCount,OP_2OVER)) + case OP_2SWAP :: t => loop(op2Swap(p),calcOpCount(opCount,OP_2SWAP)) //arithmetic operations - case OP_ADD :: t => loop(opAdd(p)) - case OP_1ADD :: t => loop(op1Add(p)) - case OP_1SUB :: t => loop(op1Sub(p)) - case OP_SUB :: t => loop(opSub(p)) - case OP_ABS :: t => loop(opAbs(p)) - case OP_NEGATE :: t => loop(opNegate(p)) - case OP_NOT :: t => loop(opNot(p)) - case OP_0NOTEQUAL :: t => loop(op0NotEqual(p)) - case OP_BOOLAND :: t => loop(opBoolAnd(p)) - case OP_BOOLOR :: t => loop(opBoolOr(p)) - case OP_NUMEQUAL :: t => loop(opNumEqual(p)) - case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(p)) - case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(p)) - case OP_LESSTHAN :: t => loop(opLessThan(p)) - case OP_GREATERTHAN :: t => loop(opGreaterThan(p)) - case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(p)) - case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(p)) - case OP_MIN :: t => loop(opMin(p)) - case OP_MAX :: t => loop(opMax(p)) - case OP_WITHIN :: t => loop(opWithin(p)) + case OP_ADD :: t => loop(opAdd(p),calcOpCount(opCount,OP_ADD)) + case OP_1ADD :: t => loop(op1Add(p),calcOpCount(opCount,OP_1ADD)) + case OP_1SUB :: t => loop(op1Sub(p),calcOpCount(opCount,OP_1SUB)) + case OP_SUB :: t => loop(opSub(p),calcOpCount(opCount,OP_SUB)) + case OP_ABS :: t => loop(opAbs(p),calcOpCount(opCount,OP_ABS)) + case OP_NEGATE :: t => loop(opNegate(p),calcOpCount(opCount,OP_NEGATE)) + case OP_NOT :: t => loop(opNot(p),calcOpCount(opCount,OP_NOT)) + case OP_0NOTEQUAL :: t => loop(op0NotEqual(p),calcOpCount(opCount,OP_0NOTEQUAL)) + case OP_BOOLAND :: t => loop(opBoolAnd(p),calcOpCount(opCount,OP_BOOLAND)) + case OP_BOOLOR :: t => loop(opBoolOr(p),calcOpCount(opCount,OP_BOOLOR)) + case OP_NUMEQUAL :: t => loop(opNumEqual(p),calcOpCount(opCount,OP_NUMEQUAL)) + case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(p),calcOpCount(opCount,OP_NUMEQUALVERIFY)) + case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(p),calcOpCount(opCount,OP_NUMNOTEQUAL)) + case OP_LESSTHAN :: t => loop(opLessThan(p),calcOpCount(opCount,OP_LESSTHAN)) + case OP_GREATERTHAN :: t => loop(opGreaterThan(p),calcOpCount(opCount,OP_GREATERTHAN)) + case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(p),calcOpCount(opCount,OP_LESSTHANOREQUAL)) + case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(p),calcOpCount(opCount,OP_GREATERTHANOREQUAL)) + case OP_MIN :: t => loop(opMin(p),calcOpCount(opCount,OP_MIN)) + case OP_MAX :: t => loop(opMax(p),calcOpCount(opCount,OP_MAX)) + case OP_WITHIN :: t => loop(opWithin(p),calcOpCount(opCount,OP_WITHIN)) //bitwise operations - case OP_EQUAL :: t => - val newProgram = opEqual(p) - loop(newProgram) + case OP_EQUAL :: t => loop(opEqual(p),calcOpCount(opCount,OP_EQUAL)) - case OP_EQUALVERIFY :: t => loop(opEqualVerify(p)) + case OP_EQUALVERIFY :: t => loop(opEqualVerify(p),calcOpCount(opCount,OP_EQUALVERIFY)) - case OP_0 :: t => loop(ScriptProgram(p, ScriptNumber.zero :: p.stack, t)) + case OP_0 :: t => loop(ScriptProgram(p, ScriptNumber.zero :: p.stack, t),calcOpCount(opCount,OP_0)) case (scriptNumberOp : ScriptNumberOperation) :: t => - loop(ScriptProgram(p, ScriptNumber(scriptNumberOp.underlying) :: p.stack, t)) + loop(ScriptProgram(p, ScriptNumber(scriptNumberOp.underlying) :: p.stack, t),calcOpCount(opCount,scriptNumberOp)) case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => - loop(pushScriptNumberBytesToStack(p)) + loop(pushScriptNumberBytesToStack(p),calcOpCount(opCount,bytesToPushOntoStack)) case (scriptNumber: ScriptNumber) :: t => - loop(ScriptProgram(p, scriptNumber :: p.stack, t)) - case OP_PUSHDATA1 :: t => loop(opPushData1(p)) - case OP_PUSHDATA2 :: t => loop(opPushData2(p)) - case OP_PUSHDATA4 :: t => loop(opPushData4(p)) + loop(ScriptProgram(p, scriptNumber :: p.stack, t),calcOpCount(opCount,scriptNumber)) + case OP_PUSHDATA1 :: t => loop(opPushData1(p),calcOpCount(opCount,OP_PUSHDATA1)) + case OP_PUSHDATA2 :: t => loop(opPushData2(p),calcOpCount(opCount,OP_PUSHDATA2)) + case OP_PUSHDATA4 :: t => loop(opPushData4(p),calcOpCount(opCount,OP_PUSHDATA4)) - case (x : ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t)) + case (x : ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t),calcOpCount(opCount,x)) //control operations - case OP_IF :: t => loop(opIf(p)) - case OP_NOTIF :: t => loop(opNotIf(p)) - case OP_ELSE :: t => loop(opElse(p)) - case OP_ENDIF :: t => loop(opEndIf(p)) - case OP_RETURN :: t => loop(opReturn(p)) + case OP_IF :: t => loop(opIf(p),calcOpCount(opCount,OP_IF)) + case OP_NOTIF :: t => loop(opNotIf(p),calcOpCount(opCount,OP_NOTIF)) + case OP_ELSE :: t => loop(opElse(p),calcOpCount(opCount,OP_ELSE)) + case OP_ENDIF :: t => loop(opEndIf(p),calcOpCount(opCount,OP_ENDIF)) + case OP_RETURN :: t => loop(opReturn(p),calcOpCount(opCount,OP_RETURN)) - case OP_VERIFY :: t => loop(opVerify(p)) + case OP_VERIFY :: t => loop(opVerify(p),calcOpCount(opCount,OP_VERIFY)) //crypto operations - case OP_HASH160 :: t => loop(opHash160(p)) - case OP_CHECKSIG :: t => loop(opCheckSig(p)) - case OP_CHECKSIGVERIFY :: t => loop(opCheckSigVerify(p)) - case OP_SHA1 :: t => loop(opSha1(p)) - case OP_RIPEMD160 :: t => loop(opRipeMd160(p)) - case OP_SHA256 :: t => loop(opSha256(p)) - case OP_HASH256 :: t => loop(opHash256(p)) - case OP_CODESEPARATOR :: t => loop(opCodeSeparator(p)) + case OP_HASH160 :: t => loop(opHash160(p),calcOpCount(opCount,OP_HASH160)) + case OP_CHECKSIG :: t => loop(opCheckSig(p),calcOpCount(opCount,OP_CHECKSIG)) + case OP_CHECKSIGVERIFY :: t => loop(opCheckSigVerify(p),calcOpCount(opCount,OP_CHECKSIGVERIFY)) + case OP_SHA1 :: t => loop(opSha1(p),calcOpCount(opCount,OP_SHA1)) + case OP_RIPEMD160 :: t => loop(opRipeMd160(p),calcOpCount(opCount,OP_RIPEMD160)) + case OP_SHA256 :: t => loop(opSha256(p),calcOpCount(opCount,OP_SHA256)) + case OP_HASH256 :: t => loop(opHash256(p),calcOpCount(opCount,OP_HASH256)) + case OP_CODESEPARATOR :: t => loop(opCodeSeparator(p),calcOpCount(opCount,OP_CODESEPARATOR)) case OP_CHECKMULTISIG :: t => opCheckMultiSig(p) match { case newProgram : ExecutedScriptProgram => //script was marked invalid for other reasons, don't need to update the opcount - loop(newProgram) - case newProgram : ExecutionInProgressScriptProgram => - opCount = opCount + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt - loop(newProgram) - case newProgram : PreExecutionScriptProgram => - opCount = opCount + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt - loop(newProgram) + loop(newProgram,opCount) + case newProgram @ (_ : ExecutionInProgressScriptProgram | _ : PreExecutionScriptProgram) => + val newOpCount = calcOpCount(opCount,OP_CHECKMULTISIG) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt + loop(newProgram,newOpCount) } case OP_CHECKMULTISIGVERIFY :: t => opCheckMultiSigVerify(p) match { case newProgram : ExecutedScriptProgram => //script was marked invalid for other reasons, don't need to update the opcount - loop(newProgram) - case newProgram : ExecutionInProgressScriptProgram => - opCount = opCount + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt - loop(newProgram) - case newProgram : PreExecutionScriptProgram => - opCount = opCount + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt - loop(newProgram) + loop(newProgram,opCount) + case newProgram @ (_ : ExecutionInProgressScriptProgram | _ : PreExecutionScriptProgram) => + val newOpCount = calcOpCount(opCount,OP_CHECKMULTISIGVERIFY) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt + loop(newProgram,newOpCount) } //reserved operations case OP_NOP :: t => //script discourage upgradeable flag does not apply to a OP_NOP - loop(ScriptProgram(p, p.stack, t)) + loop(ScriptProgram(p, p.stack, t),calcOpCount(opCount,OP_NOP)) //if we see an OP_NOP and the DISCOURAGE_UPGRADABLE_OP_NOPS flag is set we must fail our program case (nop: NOP) :: t if ScriptFlagUtil.discourageUpgradableNOPs(p.flags) => logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set") - loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs)) - case (nop: NOP) :: t => loop(ScriptProgram(p, p.stack, t)) + loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,nop)) + case (nop: NOP) :: t => loop(ScriptProgram(p, p.stack, t),calcOpCount(opCount,nop)) case OP_RESERVED :: t => logger.error("OP_RESERVED automatically marks transaction invalid") - loop(ScriptProgram(p,ScriptErrorBadOpCode)) + loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED)) case OP_VER :: t => logger.error("Transaction is invalid when executing OP_VER") - loop(ScriptProgram(p,ScriptErrorBadOpCode)) + loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_VER)) case OP_RESERVED1 :: t => logger.error("Transaction is invalid when executing OP_RESERVED1") - loop(ScriptProgram(p,ScriptErrorBadOpCode)) + loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED1)) case OP_RESERVED2 :: t => logger.error("Transaction is invalid when executing OP_RESERVED2") - loop(ScriptProgram(p,ScriptErrorBadOpCode)) + loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED2)) case (reservedOperation : ReservedOperation) :: t => logger.error("Undefined operation found which automatically fails the script: " + reservedOperation) - loop(ScriptProgram(p,ScriptErrorBadOpCode)) + loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,reservedOperation)) //splice operations - case OP_SIZE :: t => loop(opSize(p)) + case OP_SIZE :: t => loop(opSize(p),calcOpCount(opCount,OP_SIZE)) //locktime operations case OP_CHECKLOCKTIMEVERIFY :: t => //check if CLTV is enforced yet - if (ScriptFlagUtil.checkLockTimeVerifyEnabled(p.flags)) loop(opCheckLockTimeVerify(p)) + if (ScriptFlagUtil.checkLockTimeVerifyEnabled(p.flags)) loop(opCheckLockTimeVerify(p),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY)) //if not, check to see if we should discourage p else if (ScriptFlagUtil.discourageUpgradableNOPs(p.flags)) { logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set") - loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs)) + loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY)) } //in this case, just reat OP_CLTV just like a NOP and remove it from the stack - else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script)) + else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY)) case OP_CHECKSEQUENCEVERIFY :: t => //check if CLTV is enforced yet - if (ScriptFlagUtil.checkSequenceVerifyEnabled(p.flags)) loop(opCheckSequenceVerify(p)) + if (ScriptFlagUtil.checkSequenceVerifyEnabled(p.flags)) loop(opCheckSequenceVerify(p),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY)) //if not, check to see if we should discourage p else if (ScriptFlagUtil.discourageUpgradableNOPs(p.flags)) { logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set") - loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs)) + loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY)) } //in this case, just reat OP_CSV just like a NOP and remove it from the stack - else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script)) + else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY)) //no more script operations to run, return whether the program is valid and the final state of the program - case Nil => loop(ScriptProgram.toExecutedProgram(p)) + case Nil => loop(ScriptProgram.toExecutedProgram(p),opCount) case h :: t => throw new RuntimeException(h + " was unmatched") } } @@ -413,6 +395,16 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con currencyUnit >= CurrencyUnits.zero && currencyUnit <= Consensus.maxMoney } + /** + * Calculates the new op count after the execution of the given [[ScriptToken]] + * @param oldOpCount + * @param token + * @return + */ + private def calcOpCount(oldOpCount: Int, token: ScriptToken):Int = BitcoinScriptUtil.countsTowardsScriptOpLimit(token) match { + case true => oldOpCount + 1 + case false => oldOpCount + } } diff --git a/src/test/scala/org/bitcoins/core/protocol/blockchain/BlockTest.scala b/src/test/scala/org/bitcoins/core/protocol/blockchain/BlockTest.scala index 3c2cdf5675..27961773e9 100644 --- a/src/test/scala/org/bitcoins/core/protocol/blockchain/BlockTest.scala +++ b/src/test/scala/org/bitcoins/core/protocol/blockchain/BlockTest.scala @@ -1,5 +1,6 @@ package org.bitcoins.core.protocol.blockchain +import org.bitcoins.core.protocol.CompactSizeUInt import org.bitcoins.core.util.BitcoinSLogger import org.scalatest.{FlatSpec, MustMatchers} @@ -16,4 +17,5 @@ class BlockTest extends FlatSpec with MustMatchers with BitcoinSLogger { val block = Block(hex) block.hex must be (hex) } + }