Merge pull request #5 from Christewart/script_program_refactoring

Script program refactoring
This commit is contained in:
Chris Stewart 2016-04-20 14:32:47 -05:00
commit ee39a2f815
22 changed files with 706 additions and 481 deletions

View file

@ -77,8 +77,10 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
SignatureValidationFailureIncorrectSignatures
}
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
logger.info("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
SignatureValidationFailureIncorrectSignatures
SignatureValidationFailureSignatureCount
}
else if (!sigs.isEmpty && !pubKeys.isEmpty) {
val sig = sigs.head
@ -91,6 +93,8 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
multiSignatureEvaluator(txSignatureComponent, sigs,pubKeys.tail,flags, requiredSigs)
case SignatureValidationFailureNotStrictDerEncoding =>
SignatureValidationFailureNotStrictDerEncoding
case SignatureValidationFailureSignatureCount =>
SignatureValidationFailureSignatureCount
}
} else if (sigs.isEmpty) {
//means that we have checked all of the sigs against the public keys

View file

@ -36,3 +36,13 @@ case object SignatureValidationFailureNotStrictDerEncoding extends TransactionSi
case object SignatureValidationFailureIncorrectSignatures extends TransactionSignatureCheckerResult {
def isValid = false
}
/**
* This indicates that the signature validation failed because we have more signatures left to check
* than public keys remaining to check them against
* see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915
*/
case object SignatureValidationFailureSignatureCount extends TransactionSignatureCheckerResult {
def isValid = false
}

View file

@ -4,13 +4,14 @@ import org.scalacoin.crypto.{TransactionSignatureComponentFactory, TransactionSi
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.constant._
import org.scalacoin.script.error.ScriptError
import org.scalacoin.script.flag.ScriptFlag
import org.scalacoin.util.Factory
/**
* Created by chris on 2/3/16.
*/
trait ScriptProgram {
sealed trait ScriptProgram {
/**
@ -59,20 +60,7 @@ trait ScriptProgram {
def flags : Seq[ScriptFlag]
/**
* A function to determine if the transaction is valid or not
*
* @return
*/
def isValid : Boolean
/**
* The index of the last OP_CODESEPARATOR
*
* @return
*/
def lastCodeSeparator : Int
/**
* Returns if the stack top is true
*
* @return
@ -91,14 +79,77 @@ trait ScriptProgram {
else if (!stack.headOption.isDefined) true
else false
}
}
/**
* This represents a ScriptProgram before any script operations have been executed in the interpreter
*/
sealed trait PreExecutionScriptProgram extends ScriptProgram
sealed trait ExecutionInProgressScriptProgram extends ScriptProgram {
/**
* The index of the last OP_CODESEPARATOR
* @return
*/
def lastCodeSeparator : Int
}
sealed trait ExecutedScriptProgram extends ScriptProgram {
/**
* Indicates if the program has encountered a ScriptError in its execution
* @return
*/
def error : Option[ScriptError]
}
/**
* Factory companion object for ScriptProgram
*/
object ScriptProgram {
/**
* Implentation type for a script program that has not been executed at all
* @param txSignatureComponent
* @param stack
* @param script
* @param originalScript
* @param altStack
* @param flags
*/
private sealed case class PreExecutionScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag]) extends PreExecutionScriptProgram
private sealed case class ScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], isValid : Boolean = true, lastCodeSeparator : Int = 0) extends ScriptProgram
/**
* Implementation type for a script program that is currently being executed by the script interpreter
* @param txSignatureComponent
* @param stack
* @param script
* @param originalScript
* @param altStack
* @param flags
* @param lastCodeSeparator
*/
private sealed case class ExecutionInProgressScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], lastCodeSeparator : Int = 0) extends ExecutionInProgressScriptProgram
/**
* The implementation type for a script program that is finished being executed by the script interpreter
* @param txSignatureComponent
* @param stack
* @param script
* @param originalScript
* @param altStack
* @param flags
* @param error
*/
private sealed case class ExecutedScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], error : Option[ScriptError]) extends ExecutedScriptProgram
//indicates whether the script or the stack needs to be updated
@ -107,37 +158,45 @@ object ScriptProgram {
case object Script extends UpdateIndicator
case object AltStack extends UpdateIndicator
/**
* Changes the validity of a script program
*
* @param oldProgram
* @param valid
* @return
*/
def factory(oldProgram : ScriptProgram, valid : Boolean) : ScriptProgram = {
ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack,oldProgram.script, oldProgram.originalScript,
oldProgram.altStack, oldProgram.flags, valid, oldProgram.lastCodeSeparator)
* Sets an error on the script program
* @param oldProgram the program who has hit an invalid state
* @param error the error that thet program hit while being executed in the script interpreter
* @return the ExecutedScriptProgram with the given error set inside of the trait
*/
def factory(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = oldProgram match {
case program : PreExecutionScriptProgram =>
throw new RuntimeException("We cannot set an error on the script program before it is executed")
case program : ExecutionInProgressScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
program.altStack, program.flags, Some(error))
case program : ExecutedScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
program.altStack, program.flags, Some(error))
}
/**
* Updates the program script verify flags
*
* @param oldProgram
* @param flags
* @return
*/
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = {
ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack,oldProgram.script, oldProgram.originalScript,
oldProgram.altStack, flags, oldProgram.isValid, oldProgram.lastCodeSeparator)
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent,program.stack,program.script,program.originalScript,
program.altStack,flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
program.altStack, flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the script flags on a program that has been executed")
}
/**
* Changes the tokens in either the Stack or the Script dependong in the indicactor
*
* Changes the tokens in either the Stack or the Script depending in the indicator
* @param oldProgram
* @param tokens
* @param indicator
@ -145,115 +204,103 @@ object ScriptProgram {
*/
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
indicator match {
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent,tokens.toList, oldProgram.script,
oldProgram.originalScript,oldProgram.altStack, oldProgram.flags,
oldProgram.isValid, oldProgram.lastCodeSeparator)
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags,
oldProgram.isValid, oldProgram.lastCodeSeparator)
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript,tokens.toList,oldProgram.flags,
oldProgram.isValid, oldProgram.lastCodeSeparator)
case Stack =>
oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, tokens.toList,program.script,program.originalScript,
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent,tokens.toList,program.script,program.originalScript,
program.altStack,program.flags,program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update stack for program that has been fully executed")
}
case Script =>
oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,tokens.toList,program.originalScript,
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, tokens.toList, program.originalScript,
program.altStack, program.flags)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the script for a program that has been fully executed")
}
case AltStack => oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
tokens.toList,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
tokens.toList, program.flags)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the alt stack for a program that has been fully executed")
}
}
}
/**
* Changes the stack tokens and script tokens in a ScriptProgram
*
* @param oldProgram
* @param stackTokens
* @param scriptTokens
* @return
*/
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = {
val updatedStack = factory(oldProgram,stackTokens,Stack)
val updatedScript = factory(updatedStack,scriptTokens,Script)
val updatedStack = apply(oldProgram,stackTokens,Stack)
val updatedScript = apply(updatedStack,scriptTokens,Script)
updatedScript
}
/**
* Updates the programs stack tokens, script tokens & script verify flags
*
* @param oldProgram
* @param stackTokens
* @param scriptTokens
* @param flags
* @return
*/
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
val updatedStackAndScript = factory(oldProgram,stackTokens,scriptTokens)
factory(updatedStackAndScript,flags)
}
/**
* Updates the last OP_CODESEPARATOR index
*
* @param oldProgram
* @param lastCodeSeparator
* @return
*/
def factory(oldProgram : ScriptProgram, lastCodeSeparator : Int) : ScriptProgram = {
ScriptProgramImpl(oldProgram.txSignatureComponent,
def factory(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
oldProgram.altStack, oldProgram.flags, isValid = oldProgram.isValid, lastCodeSeparator = lastCodeSeparator)
oldProgram.altStack, oldProgram.flags,lastCodeSeparator)
}
/**
* Updates the tokens in either the stack or script and the last OP_CODESEPARATOR index
*
* @param oldProgram
* @param tokens
* @param indicator
* @param lastCodeSeparator
* @return
*/
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ScriptProgram = {
indicator match {
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent,
tokens.toList, oldProgram.script, oldProgram.originalScript, oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script,oldProgram.originalScript,
tokens.toList, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
def factory(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
val updatedIndicator = apply(oldProgram, tokens, indicator)
updatedIndicator match {
case e : ExecutionInProgressScriptProgram =>
apply(e,lastCodeSeparator)
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
throw new RuntimeException("We must have a ExecutionInProgressScriptProgram to update the last OP_CODESEPARATOR index")
}
}
/**
* Updates the stack, script and validity of a script program
*
* @param oldProgram
* @param stack
* @param script
* @param valid
* @return
*/
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], valid : Boolean) : ScriptProgram = {
val stackAndScriptUpdate = factory(oldProgram, stack,script)
factory(stackAndScriptUpdate, valid)
}
/**
* Updates the stack, script
*
* Updates the stack, script, alt stack of the given oldProgram
* @param oldProgram
* @param stack
* @param script
* @param altStack
* @param updateIndicator
* @return
*/
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
updateIndicator: UpdateIndicator) : ScriptProgram = {
ScriptProgramImpl(oldProgram.txSignatureComponent,
stack.toList,script.toList,oldProgram.originalScript,altStack.toList,oldProgram.flags, oldProgram.isValid,oldProgram.lastCodeSeparator)
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken]) : ScriptProgram = {
val updatedProgramStack = apply(oldProgram,stack, Stack)
val updatedProgramScript = apply(updatedProgramStack, script, Script)
val updatedProgramAltStack = apply(updatedProgramScript, altStack, AltStack)
updatedProgramAltStack
}
/**
* Creates a new script program that can be used to verify if a transaction at the given inputIndex
* spends a given scriptPubKey correctly. Assumes that the script to be executed is the
@ -266,9 +313,9 @@ object ScriptProgram {
* @return the script program representing all of this information
*/
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int,
flags : Seq[ScriptFlag]) : ScriptProgram = {
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex).scriptSignature.asm
factory(transaction,scriptPubKey,inputIndex,script.toList,flags)
apply(transaction,scriptPubKey,inputIndex,script.toList,flags)
}
/**
@ -283,9 +330,9 @@ object ScriptProgram {
* @return the script program representing all of this information
*/
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, script : Seq[ScriptToken],
flags : Seq[ScriptFlag]) : ScriptProgram = {
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
val txSignatureComponent = TransactionSignatureComponentFactory.factory(transaction,inputIndex,scriptPubKey,flags)
ScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
PreExecutionScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
}
@ -293,7 +340,6 @@ object ScriptProgram {
* The intention for this factory function is to allow us to create a program that already has a stack state. This
* is useful for after execution of a scriptSig, copying the stack into this program with the scriptPubKey read to
* run inside the script variable
*
* @param transaction the transaction being checked
* @param scriptPubKey the scriptPubKey which the input is spending
* @param inputIndex the input's index inside of the transaction we are spending
@ -304,7 +350,7 @@ object ScriptProgram {
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
val program = factory(transaction,scriptPubKey,inputIndex,script,flags)
factory(program,stack,Stack)
apply(program,stack,Stack)
}
@ -319,32 +365,79 @@ object ScriptProgram {
* @return
*/
def factory(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram = {
factory(txSignatureComponent.transaction,txSignatureComponent.scriptPubKey,txSignatureComponent.inputIndex,
apply(txSignatureComponent.transaction,txSignatureComponent.scriptPubKey,txSignatureComponent.inputIndex,
stack,script,txSignatureComponent.flags)
}
def apply(oldProgram : ScriptProgram, valid : Boolean) : ScriptProgram = factory(oldProgram, valid)
def apply(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = factory(oldProgram, error)
def apply(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = factory(oldProgram, flags)
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram =
factory(oldProgram, tokens, indicator)
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram =
factory(oldProgram, stackTokens, scriptTokens)
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram =
factory(oldProgram, stackTokens, scriptTokens, flags)
def apply(oldProgram : ScriptProgram, lastCodeSeparator : Int) : ScriptProgram = factory(oldProgram, lastCodeSeparator)
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ScriptProgram =
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = factory(oldProgram, lastCodeSeparator)
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram =
factory(oldProgram, tokens, indicator, lastCodeSeparator)
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], valid : Boolean) : ScriptProgram =
factory(oldProgram, stack, script, valid)
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
updateIndicator: UpdateIndicator) : ScriptProgram = factory(oldProgram, stack, script, altStack, updateIndicator)
updateIndicator: UpdateIndicator) : ScriptProgram = factory(oldProgram, stack, script, altStack)
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int,
flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, flags)
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, flags)
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, script : Seq[ScriptToken],
flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, script, flags)
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, script, flags)
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, stack, script, flags)
def apply(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram =
factory(txSignatureComponent, stack, script)
/**
* Changes a program that is being executed inside o
* @param executionInProgressScriptProgram
* @return
*/
def toExecutedProgram(executionInProgressScriptProgram: ExecutionInProgressScriptProgram) : ExecutedScriptProgram = {
ExecutedScriptProgramImpl(executionInProgressScriptProgram.txSignatureComponent, executionInProgressScriptProgram.stack,
executionInProgressScriptProgram.script,executionInProgressScriptProgram.originalScript,executionInProgressScriptProgram.altStack,
executionInProgressScriptProgram.flags,None)
}
/**
* Takes a script program that is pre execution and changes it to an execution in progress script program
* @param preExecutionScriptProgram
* @return
*/
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram) : ExecutionInProgressScriptProgram = {
toExecutionInProgress(preExecutionScriptProgram,None)
}
/**
* Changes a pre execution script program to a execution in progress script program with the given stack state
* @param preExecutionScriptProgram
* @param stack
* @return
*/
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[List[ScriptToken]]) : ExecutionInProgressScriptProgram = {
stack match {
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,stackTokens,preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, 0)
case None =>
ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,preExecutionScriptProgram.stack,preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, 0)
}
}
}

View file

@ -1,6 +1,7 @@
package org.scalacoin.script.arithmetic
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.scalacoin.script.error.{ScriptErrorUnknownError, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.BitcoinSUtil
@ -142,7 +143,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_NUMEQUALVERIFY")
if (program.stack.size < 2) {
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val numEqualProgram = ScriptProgram(program, program.stack, OP_NUMEQUAL :: program.script.tail)
val numEqualResult = opNumEqual(numEqualProgram)
@ -225,7 +226,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
if (program.stack.size < 2) {
logger.error("OP_MIN requires at least two stack elements")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val b = program.stack.head
val a = program.stack.tail.head
@ -252,7 +253,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_MAX")
if (program.stack.size < 2) {
logger.error("OP_MAX requires at least two stack elements")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val b = program.stack.head
val a = program.stack.tail.head
@ -281,7 +282,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_WITHIN")
if (program.stack.size < 3) {
logger.error("OP_WITHIN requires at least 3 elements on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val c = program.stack.head
val b = program.stack.tail.head
@ -319,7 +320,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
program.stack.headOption match {
case None =>
logger.error("We need one stack element for performing a unary arithmetic operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Some(s : ScriptNumber) =>
if (checkBitcoinIntByteSize(s)) {
val newScriptNumber = op(s)
@ -327,15 +328,19 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
}
else {
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
ScriptProgram(program,false)
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
ScriptProgram(program,ScriptErrorUnknownError)
}
case Some(s : ScriptConstant) =>
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex))
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
performUnaryArithmeticOperation(newProgram, op)
case Some(s : ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Stack top must be a script number to perform an arithmetic operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnknownError)
}
}
@ -348,7 +353,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
private def performBinaryArithmeticOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = {
if (program.stack.size < 2) {
logger.error("We must have two elements to perform a binary arithmetic operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
(program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) =>
@ -357,8 +362,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
}
else {
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnknownError)
}
case (x : ScriptConstant, y : ScriptNumber) =>
//interpret x as a number
@ -377,8 +384,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnknownError)
}
}
@ -395,7 +404,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
if (program.stack.size < 2) {
logger.error("We need two stack elements for a binary boolean operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
(program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) =>
@ -404,8 +413,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
}
else {
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Cannot perform boolean operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnknownError)
}
case (x : ScriptConstant, y : ScriptNumber) =>
//interpret x as a number
@ -424,8 +435,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
performBinaryBooleanOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnknownError)
}
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.bitwise
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.script.control.{OP_VERIFY, ControlOperationsInterpreter}
@ -20,7 +21,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
require(program.script.headOption.isDefined && program.script.head == OP_EQUAL, "Script operation must be OP_EQUAL")
if (program.stack.size < 2) {
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val h = program.stack.head
val h1 = program.stack.tail.head
@ -65,7 +66,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
opVerify(newProgram)
case false =>
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.constant
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
import org.slf4j.LoggerFactory
@ -85,7 +86,7 @@ trait ConstantInterpreter extends BitcoinSLogger {
//check to see if we have the exact amount of bytes needed to be pushed onto the stack
//if we do not, mark the program as invalid
if (bytesNeeded == 0) ScriptProgram(program, ScriptNumberFactory.zero :: program.stack, newScript)
else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) ScriptProgram(program,false)
else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) ScriptProgram(program,ScriptErrorInvalidStackOperation)
else ScriptProgram(program, constant :: program.stack, newScript)
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.control
import org.scalacoin.script.error.{ScriptErrorVerify, ScriptErrorOpReturn, ScriptErrorInvalidStackOperation, ScriptErrorUnbalancedConditional}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util._
@ -25,10 +26,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
logger.debug("Parsed binary tree: " + binaryTree)
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
logger.error("We do not have a matching OP_ENDIF for every OP_IF we have")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_IF")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
else if (program.stackTopIsTrue) {
logger.debug("OP_IF stack top was true")
@ -60,10 +61,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
logger.error("We do not have a matching OP_ENDIF for every OP_NOTIF we have")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_NOTIF")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else if (program.stackTopIsTrue) {
//remove the OP_NOTIF
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
@ -114,7 +115,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
//means we do not have a matching OP_IF for our OP_ENDIF
logger.error("We do not have a matching OP_IF/OP_NOTIF for every OP_ENDIF we have")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else ScriptProgram(program, program.stack,program.script.tail)
}
@ -130,7 +131,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
*/
def opReturn(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_RETURN)
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorOpReturn)
}
@ -141,14 +142,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
*/
def opVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
program.script.size > 0 match {
program.stack.size > 0 match {
case true =>
logger.debug("Stack for OP_VERIFY: " + program.stack)
if (program.stackTopIsFalse) ScriptProgram(program,false)
if (program.stackTopIsFalse) ScriptProgram(program,ScriptErrorVerify)
else ScriptProgram(program, program.stack.tail,program.script.tail)
case false =>
logger.error("OP_VERIFY requires an element to be on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}

View file

@ -4,8 +4,9 @@ import org.scalacoin.crypto._
import org.scalacoin.protocol.script._
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.scalacoin.script.error.{ScriptErrorSigCount, ScriptErrorSigNullDummy, ScriptErrorSigDer, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.flag.{ScriptVerifyNullDummy, ScriptVerifyDerSig}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.{ExecutionInProgressScriptProgram, ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
import org.slf4j.LoggerFactory
@ -80,7 +81,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
if (program.stack.size < 2) {
logger.error("OP_CHECKSIG requires at lest two stack elements")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val pubKey = ECFactory.publicKey(program.stack.head.bytes)
val signature = ECFactory.digitalSignature(program.stack.tail.head.bytes)
@ -91,7 +92,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
//script verification fails since the sig is not strictly der encoded
logger.warn("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(program,false)
ScriptProgram(program,ScriptErrorSigDer)
} else {
val restOfStack = program.stack.tail.tail
@ -103,10 +104,11 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
case SignatureValidationSuccess => ScriptProgram(program,
ScriptTrue :: restOfStack,program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
ScriptProgram(program, ScriptFalse :: restOfStack,
program.script.tail,SignatureValidationFailureNotStrictDerEncoding.isValid)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
ScriptProgram(program, ScriptFalse :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
ScriptProgram(program, ScriptFalse :: restOfStack,program.script.tail)
}
}
}
@ -131,7 +133,10 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
}
val indexOfOpCodeSeparator = fullScript.indexOf(OP_CODESEPARATOR)
require(indexOfOpCodeSeparator != -1,"The script we searched MUST contain an OP_CODESEPARTOR. Script: " + fullScript)
ScriptProgram(program,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
val e = program match {
case e : ExecutionInProgressScriptProgram => e
}
ScriptProgram(e,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
}
@ -153,10 +158,10 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
if (program.flags.contains(ScriptVerifyNullDummy) && program.txSignatureComponent.scriptSignature.asm.head != OP_0) {
logger.warn("Script flag null dummy was set however the first element in the script signature was not an OP_0")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorSigNullDummy)
} else if (program.stack.size < 3) {
logger.error("OP_CHECKMULTISIG requires at least 3 stack elements")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : Int = program.stack.head match {
@ -201,11 +206,14 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
//set the valid flag to false on the script
//see BIP66 for more information on this
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgram(program, restOfStack, program.script.tail,false)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(program, ScriptFalse :: restOfStack, program.script.tail)
case SignatureValidationFailureSignatureCount =>
//means that we did not have enough signatures for OP_CHECKMULTISIG
ScriptProgram(program, ScriptErrorSigCount)
}
}
}
@ -218,13 +226,17 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
*/
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY")
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack)
val programFromOpVerify = opVerify(programFromOpCheckMultiSig)
programFromOpVerify
if (program.stack.size < 3) {
logger.error("Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack)
val programFromOpVerify = opVerify(programFromOpCheckMultiSig)
programFromOpVerify
}
}
@ -243,7 +255,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
ScriptProgram(program, hash :: program.stack.tail, program.script.tail)
} else {
logger.error("We must have the stack top defined to execute a hash function")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}

View file

@ -2,10 +2,11 @@ package org.scalacoin.script.interpreter
import org.scalacoin.protocol.script._
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.error._
import org.scalacoin.script.flag._
import org.scalacoin.script.locktime.{OP_CHECKLOCKTIMEVERIFY, LockTimeInterpreter}
import org.scalacoin.script.splice._
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
import org.scalacoin.script.arithmetic._
import org.scalacoin.script.bitwise._
import org.scalacoin.script.constant._
@ -55,186 +56,193 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
* @return program the final state of the program after being evaluated by the interpreter
*/
@tailrec
def loop(program : ScriptProgram) : (Boolean,ScriptProgram) = {
def loop(program : ScriptProgram) : (Boolean,ExecutedScriptProgram) = {
logger.debug("Stack: " + program.stack)
logger.debug("Script: " + program.script)
if (program.script.headOption.isDefined &&
BitcoinScriptUtil.countsTowardsScriptOpLimit(program.script.head)) opCount = opCount + 1
if (opCount > maxScriptOps) {
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)
(false,ScriptProgram(program,false))
} else if (program.script.flatMap(_.bytes).size > 10000) {
loop(ScriptProgram(program,ScriptErrorOpCount))
} 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")
(false,ScriptProgram(program,false))
program match {
case p : PreExecutionScriptProgram =>
loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize))
case _ : ExecutionInProgressScriptProgram | _ : ExecutedScriptProgram =>
loop(ScriptProgram(program, ScriptErrorScriptSize))
}
} else {
program.script match {
//if at any time we see that the program is not valid
//cease script execution
case _ if !program.isValid =>
logger.error("Script program was marked as invalid: " + program)
(false, ScriptProgram(program, false))
case _ if !program.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")
(false, ScriptProgram(program, false))
//disabled splice operation
case _ if !program.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).isEmpty =>
logger.error("Script is invalid because it contains a disabled splice operation")
(false, ScriptProgram(program, false))
//disabled bitwise operations
case _ if !program.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty =>
logger.error("Script is invalid because it contains a disabled bitwise operation")
(false, ScriptProgram(program, false))
//disabled arithmetic operations
case _ if !program.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")
(false, ScriptProgram(program, false))
//program cannot contain a push operation > 520 bytes
case _ if (program.script.exists(token => token.bytes.size > 520)) =>
logger.error("We have a script constant that is larger than 520 bytes, this is illegal: " + program.script)
loop(ScriptProgram(program, false))
//program stack size cannot be greater than 1000 elements
case _ if ((program.stack.size + program.altStack.size) > 1000) =>
logger.error("We cannot have a stack + alt stack size larger than 1000 elements")
loop(ScriptProgram(program, false))
//stack operations
case OP_DUP :: t => loop(opDup(program))
case OP_DEPTH :: t => loop(opDepth(program))
case OP_TOALTSTACK :: t => loop(opToAltStack(program))
case OP_FROMALTSTACK :: t => loop(opFromAltStack(program))
case OP_DROP :: t => loop(opDrop(program))
case OP_IFDUP :: t => loop(opIfDup(program))
case OP_NIP :: t => loop(opNip(program))
case OP_OVER :: t => loop(opOver(program))
case OP_PICK :: t => loop(opPick(program))
case OP_ROLL :: t => loop(opRoll(program))
case OP_ROT :: t => loop(opRot(program))
case OP_2ROT :: t => loop(op2Rot(program))
case OP_2DROP :: t => loop(op2Drop(program))
case OP_SWAP :: t => loop(opSwap(program))
case OP_TUCK :: t => loop(opTuck(program))
case OP_2DUP :: t => loop(op2Dup(program))
case OP_3DUP :: t => loop(op3Dup(program))
case OP_2OVER :: t => loop(op2Over(program))
case OP_2SWAP :: t => loop(op2Swap(program))
//arithmetic operations
case OP_ADD :: t => loop(opAdd(program))
case OP_1ADD :: t => loop(op1Add(program))
case OP_1SUB :: t => loop(op1Sub(program))
case OP_SUB :: t => loop(opSub(program))
case OP_ABS :: t => loop(opAbs(program))
case OP_NEGATE :: t => loop(opNegate(program))
case OP_NOT :: t => loop(opNot(program))
case OP_0NOTEQUAL :: t => loop(op0NotEqual(program))
case OP_BOOLAND :: t => loop(opBoolAnd(program))
case OP_BOOLOR :: t => loop(opBoolOr(program))
case OP_NUMEQUAL :: t => loop(opNumEqual(program))
case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(program))
case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(program))
case OP_LESSTHAN :: t => loop(opLessThan(program))
case OP_GREATERTHAN :: t => loop(opGreaterThan(program))
case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(program))
case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(program))
case OP_MIN :: t => loop(opMin(program))
case OP_MAX :: t => loop(opMax(program))
case OP_WITHIN :: t => loop(opWithin(program))
//bitwise operations
case OP_EQUAL :: t =>
val newProgram = opEqual(program)
loop(newProgram)
case OP_EQUALVERIFY :: t => loop(opEqualVerify(program))
case (scriptNumberOp: ScriptNumberOperation) :: t =>
if (scriptNumberOp == OP_0) loop(ScriptProgram(program, ScriptNumberFactory.zero :: program.stack, t))
else loop(ScriptProgram(program, ScriptNumberFactory.fromNumber(scriptNumberOp.num) :: program.stack, t))
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => loop(pushScriptNumberBytesToStack(program))
case (scriptNumber: ScriptNumber) :: t =>
loop(ScriptProgram(program, scriptNumber :: program.stack, t))
case OP_PUSHDATA1 :: t => loop(opPushData1(program))
case OP_PUSHDATA2 :: t => loop(opPushData2(program))
case OP_PUSHDATA4 :: t => loop(opPushData4(program))
case (x : ScriptConstant) :: t => loop(ScriptProgram(program, x :: program.stack, t))
//control operations
case OP_IF :: t => loop(opIf(program))
case OP_NOTIF :: t => loop(opNotIf(program))
case OP_ELSE :: t => loop(opElse(program))
case OP_ENDIF :: t => loop(opEndIf(program))
case OP_RETURN :: t =>
val newProgram = opReturn(program)
(newProgram.isValid, newProgram)
case OP_VERIFY :: t => loop(opVerify(program))
//crypto operations
case OP_HASH160 :: t => loop(opHash160(program))
case OP_CHECKSIG :: t => loop(opCheckSig(program))
case OP_SHA1 :: t => loop(opSha1(program))
case OP_RIPEMD160 :: t => loop(opRipeMd160(program))
case OP_SHA256 :: t => loop(opSha256(program))
case OP_HASH256 :: t => loop(opHash256(program))
case OP_CODESEPARATOR :: t => loop(opCodeSeparator(program))
case OP_CHECKMULTISIG :: t => loop(opCheckMultiSig(program))
case OP_CHECKMULTISIGVERIFY :: t => loop(opCheckMultiSigVerify(program))
//reserved operations
case OP_NOP :: t =>
//script discourage upgradeable flag does not apply to a OP_NOP
loop(ScriptProgram(program, program.stack, t))
//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(program.flags) =>
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
(false, ScriptProgram(program, false))
case (nop: NOP) :: t => loop(ScriptProgram(program, program.stack, t))
case OP_RESERVED :: t =>
logger.error("OP_RESERVED automatically marks transaction invalid")
(false, program)
case OP_VER :: t =>
logger.error("Transaction is invalid when executing OP_VER")
(false, program)
case OP_RESERVED1 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED1")
(false, program)
case OP_RESERVED2 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED2")
(false, program)
case (reservedOperation : ReservedOperation) :: t =>
logger.error("Undefined operation found which automatically fails the script: " + reservedOperation)
loop(ScriptProgram(program,false))
//splice operations
case OP_SIZE :: t => loop(opSize(program))
//locktime operations
case OP_CHECKLOCKTIMEVERIFY :: t =>
//check if CLTV is enforced yet
if (ScriptFlagUtil.checkLockTimeVerifyEnabled(program.flags)) loop(opCheckLockTimeVerify(program))
//if not, check to see if we should discourage NOPs
else if (ScriptFlagUtil.discourageUpgradableNOPs(program.flags)) {
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
(false, ScriptProgram(program, false))
}
//in this case, just reat OP_CLTV just like a NOP and remove it from the stack
else loop(ScriptProgram(program, program.script.tail, ScriptProgram.Script))
//no more script operations to run, return whether the program is valid and the final state of the program
case Nil =>
program match {
case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)))
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
opCount = 0
(program.isValid, program)
(!p.error.isDefined, p)
case p : ExecutionInProgressScriptProgram =>
//increment the op count
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, ScriptErrorDisabledOpCode))
//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))
//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))
//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))
//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))
//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))
case h :: t => throw new RuntimeException(h + " was unmatched")
//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))
//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))
//bitwise operations
case OP_EQUAL :: t =>
val newProgram = opEqual(p)
loop(newProgram)
case OP_EQUALVERIFY :: t => loop(opEqualVerify(p))
case (scriptNumberOp : ScriptNumberOperation) :: t =>
if (scriptNumberOp == OP_0) loop(ScriptProgram(p, ScriptNumberFactory.zero :: p.stack, t))
else loop(ScriptProgram(p, ScriptNumberFactory.fromNumber(scriptNumberOp.num) :: p.stack, t))
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => loop(pushScriptNumberBytesToStack(p))
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))
case (x : ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t))
//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_VERIFY :: t => loop(opVerify(p))
//crypto operations
case OP_HASH160 :: t => loop(opHash160(p))
case OP_CHECKSIG :: t => loop(opCheckSig(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_CHECKMULTISIG :: t => loop(opCheckMultiSig(p))
case OP_CHECKMULTISIGVERIFY :: t => loop(opCheckMultiSigVerify(p))
//reserved operations
case OP_NOP :: t =>
//script discourage upgradeable flag does not apply to a OP_NOP
loop(ScriptProgram(p, p.stack, t))
//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))
case OP_RESERVED :: t =>
logger.error("OP_RESERVED automatically marks transaction invalid")
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
case OP_VER :: t =>
logger.error("Transaction is invalid when executing OP_VER")
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
case OP_RESERVED1 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED1")
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
case OP_RESERVED2 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED2")
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
case (reservedOperation : ReservedOperation) :: t =>
logger.error("Undefined operation found which automatically fails the script: " + reservedOperation)
loop(ScriptProgram(p,ScriptErrorBadOpCode))
//splice operations
case OP_SIZE :: t => loop(opSize(p))
//locktime operations
case OP_CHECKLOCKTIMEVERIFY :: t =>
//check if CLTV is enforced yet
if (ScriptFlagUtil.checkLockTimeVerifyEnabled(p.flags)) loop(opCheckLockTimeVerify(p))
//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))
}
//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))
//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 h :: t => throw new RuntimeException(h + " was unmatched")
}
}
}
}
val scriptSigProgram = ScriptProgram(program,Seq(),program.txSignatureComponent.scriptSignature.asm)
val (result,executedProgram) = program.txSignatureComponent.scriptSignature match {
//if the P2SH script flag is not set, we evaluate a p2sh scriptSig just like any other scriptSig
@ -251,7 +259,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
val stack = BitcoinScriptUtil.filterPushOps(scriptSig.scriptSignatureNoRedeemScript.asm.reverse)
logger.debug("P2sh stack: " + stack)
logger.debug("P2sh redeemScript: " + scriptSig.redeemScript.asm)
val p2shRedeemScriptProgram = ScriptProgram(hashesMatchProgram,stack, scriptSig.redeemScript.asm)
val p2shRedeemScriptProgram = ScriptProgram(hashesMatchProgram.txSignatureComponent,stack, scriptSig.redeemScript.asm)
loop(p2shRedeemScriptProgram)
case false =>
logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript")
@ -260,16 +268,21 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : MultiSignatureScriptSignature |
_ : NonStandardScriptSignature | _ : P2SHScriptSignature | EmptyScriptSignature =>
val scriptSigProgram = ScriptProgram(program,Seq(),program.txSignatureComponent.scriptSignature.asm)
val (scriptSigProgramIsValid,scriptSigExecutedProgram) = loop(scriptSigProgram)
logger.info("Stack state after scriptSig execution: " + scriptSigExecutedProgram.stack)
logger.info("scriptSigExecutedProgram: " + scriptSigExecutedProgram.error)
logger.info("scriptSigProgramIsValid: " + scriptSigProgramIsValid)
if (scriptSigProgramIsValid) {
logger.debug("We do not check a redeemScript against a non p2sh scriptSig")
//now run the scriptPubKey script through the interpreter with the scriptSig as the stack arguments
val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent,
scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
require(scriptPubKeyProgram.script == scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
val (scriptPubKeyProgramIsValid, scriptPubKeyExecutedProgram) = loop(scriptPubKeyProgram)
logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack)
//if the program is valid, return if the stack top is true
//else the program is false since something illegal happened during script evaluation
scriptPubKeyProgramIsValid match {

View file

@ -1,7 +1,8 @@
package org.scalacoin.script.locktime
import org.scalacoin.protocol.transaction.TransactionConstants
import org.scalacoin.script.constant.{ScriptNumberFactory, ScriptNumberImpl, ScriptNumber}
import org.scalacoin.script.constant.{ScriptToken, ScriptNumberFactory, ScriptNumberImpl, ScriptNumber}
import org.scalacoin.script.error.{ScriptError, ScriptErrorNegativeLockTime, ScriptErrorUnsatisfiedLocktime, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.util.BitcoinSLogger
@ -28,25 +29,26 @@ trait LockTimeInterpreter extends BitcoinSLogger {
"Script top must be OP_CHECKLOCKTIMEVERIFY")
if (program.stack.size == 0) {
logger.warn("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because we have no stack items")
ScriptProgram(program, program.stack, program.script.tail, false)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else if (program.txSignatureComponent.transaction.inputs(program.txSignatureComponent.inputIndex).sequence == TransactionConstants.sequence) {
logger.warn("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because the sequence number is 0xffffffff")
ScriptProgram(program, program.stack, program.script.tail, false)
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
}
else {
val isValid = program.stack.head match {
val isError : Option[ScriptError] = program.stack.head match {
case s : ScriptNumber if (s < ScriptNumberFactory.zero) =>
logger.warn("OP_CHECKLOCKTIMEVERIFY marks tx as invalid if the stack top is negative")
false
Some(ScriptErrorNegativeLockTime)
case s : ScriptNumber if (s >= ScriptNumberFactory.fromNumber(500000000) && program.txSignatureComponent.transaction.lockTime < 500000000) =>
logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top >= 500000000 & tx locktime < 500000000")
false
Some(ScriptErrorUnsatisfiedLocktime)
case s : ScriptNumber if (s < ScriptNumberFactory.fromNumber(500000000) && program.txSignatureComponent.transaction.lockTime >= 500000000) =>
logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
false
case _ => true
Some(ScriptErrorUnsatisfiedLocktime)
case _ : ScriptToken => None
}
ScriptProgram(program,program.stack, program.script.tail, isValid)
if (isError.isDefined) ScriptProgram(program,isError.get)
else ScriptProgram(program,program.stack, program.script.tail)
}
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.splice
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptOperationFactory, ScriptProgram}
import org.scalacoin.script.constant._
import Math._
@ -32,7 +33,7 @@ trait SpliceInterpreter extends BitcoinSLogger {
}
case false =>
logger.error("Must have at least 1 element on the stack for OP_SIZE")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.stack
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
@ -23,7 +24,7 @@ trait StackInterpreter extends BitcoinSLogger {
case h :: t => ScriptProgram(program, h :: program.stack, program.script.tail)
case Nil =>
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -41,7 +42,7 @@ trait StackInterpreter extends BitcoinSLogger {
program.script.tail)
case false =>
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -72,7 +73,7 @@ trait StackInterpreter extends BitcoinSLogger {
program.script.tail, program.stack.head :: program.altStack, ScriptProgram.AltStack)
case false =>
logger.error("OP_TOALTSTACK requires an element to be on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -89,7 +90,7 @@ trait StackInterpreter extends BitcoinSLogger {
program.script.tail, program.altStack.tail, ScriptProgram.AltStack)
case false =>
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -104,7 +105,7 @@ trait StackInterpreter extends BitcoinSLogger {
case true => ScriptProgram(program, program.stack.tail,program.script.tail)
case false =>
logger.error("Stack must have at least one item on it for OP_DROP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -121,10 +122,10 @@ trait StackInterpreter extends BitcoinSLogger {
case h :: _ :: t => ScriptProgram(program, h :: t, program.script.tail)
case h :: t =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Nil =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -139,10 +140,10 @@ trait StackInterpreter extends BitcoinSLogger {
program.stack match {
case _ :: h1 :: _ => ScriptProgram(program, h1 :: program.stack, program.script.tail)
case h :: t => logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Nil =>
logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -163,7 +164,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program,newStackTop :: program.stack.tail, program.script.tail)
case false =>
logger.error("The index for OP_PICK would have caused an index out of bounds exception")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -185,7 +186,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program,newStack,program.script.tail)
case false =>
logger.error("The index for OP_ROLL would have caused an index out of bounds exception")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -204,7 +205,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack,program.script.tail)
case _ =>
logger.error("Stack must have at least 3 items on it for OP_ROT")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -223,7 +224,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack,program.script.tail)
case _ =>
logger.error("OP_2ROT requires 6 elements on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -240,7 +241,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
case false =>
logger.error("OP_2DROP requires two elements to be on the stack")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -259,7 +260,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack, program.script.tail)
case false =>
logger.error("Stack must have at least 2 items on it for OP_SWAP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -280,7 +281,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 2 items on it for OP_TUCK")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -300,7 +301,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 2 items on it for OP_2DUP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -318,7 +319,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program,newStack,program.script.tail)
case _ =>
logger.error("Stack must have at least 3 items on it for OP_3DUP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -339,7 +340,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program, newStack,program.script.tail)
case _ =>
logger.error("Stack must have at least 4 items on it for OP_2OVER")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}
@ -358,7 +359,7 @@ trait StackInterpreter extends BitcoinSLogger {
ScriptProgram(program,newStack,program.script.tail)
case _ =>
logger.error("Stack must have at least 4 items on it for OP_2SWAP")
ScriptProgram(program,false)
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
}

View file

@ -1,6 +1,7 @@
package org.scalacoin.script
import org.scalacoin.script.constant.{OP_1, OP_0}
import org.scalacoin.script.flag.ScriptFlagFactory
import org.scalacoin.util.TestUtil
import org.scalatest.{MustMatchers, FlatSpec}
@ -24,14 +25,14 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
it must "update the OP_CODESEPARATOR index" in {
val index = 999
val program = ScriptProgram(TestUtil.testProgram,index)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,index)
program.lastCodeSeparator must be (999)
}
it must "update the OP_CODESEPARATOR index & stack simultaneously" in {
val index = 999
val stack = Seq(OP_0)
val program = ScriptProgram(TestUtil.testProgram,stack,ScriptProgram.Stack,index)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,ScriptProgram.Stack,index)
program.stack must be (stack)
program.lastCodeSeparator must be (index)
}
@ -39,8 +40,17 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
it must "update the OP_CODESEPARATOR index & altStack simultaneously" in {
val index = 999
val altStack = Seq(OP_0)
val program = ScriptProgram(TestUtil.testProgram,altStack,ScriptProgram.AltStack,index)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,altStack,ScriptProgram.AltStack,index)
program.altStack must be (altStack)
program.lastCodeSeparator must be (index)
}
it must "update the script program to the given stack and script" in {
val stack = List(OP_0)
val script = List(OP_1)
val program = ScriptProgram(TestUtil.transaction, TestUtil.scriptPubKey, 0, stack,script, ScriptFlagFactory.empty)
program.stack must be (stack)
program.script must be (script)
}
}

View file

@ -1,8 +1,9 @@
package org.scalacoin.script.arithmetic
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.TestUtil
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
@ -34,9 +35,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val stack = List()
val script = List(OP_1ADD)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op1Add(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Add(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -54,9 +55,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val stack = List()
val script = List(OP_1SUB)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op1Sub(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Sub(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "perform an OP_SUB corectly" in {
@ -65,16 +66,16 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opSub(program)
newProgram.stack.head must be (ScriptNumberImpl(-1))
newProgram.stack.head must be (ScriptNumberFactory.fromNumber(-1))
newProgram.script.isEmpty must be (true)
}
it must "mark a script as invalid if we have an OP_SUB with nothing on the stack" in {
val stack = List()
val script = List(OP_SUB)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opSub(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opSub(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "perform an OP_ABS on a negative number corectly" in {
@ -99,9 +100,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
it must "mark a script as invalid if we have an OP_ABS with nothing on the stack" in {
val stack = List()
val script = List(OP_ABS)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opAbs(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opAbs(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "perform an OP_NEGATE on a zero correctly" in {
@ -137,9 +138,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val stack = List()
val script = List(OP_NEGATE)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opNegate(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNegate(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "perform an OP_NOT correctly where 0 is the stack top" in {
@ -317,7 +318,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
newProgram1.stack.head must be (OP_FALSE)
newProgram1.script.isEmpty must be (true)
val stack2 = List(OP_1, ScriptNumberImpl(0))
val stack2 = List(OP_1, ScriptNumberFactory.zero)
val script2 = List(OP_LESSTHAN)
val program2 = ScriptProgram(TestUtil.testProgram, stack2,script2)
val newProgram2 = opLessThan(program2)

View file

@ -1,6 +1,6 @@
package org.scalacoin.script.bitwise
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.{ExecutedScriptProgram, ScriptProgram}
import org.scalacoin.script.arithmetic.OP_NUMEQUAL
import org.scalacoin.script.constant._
import org.scalacoin.util.TestUtil
@ -42,21 +42,22 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte
}
}
it must "evaulate OP_EQUALVERIFY to true given two of the same pub keys" in {
it must "evaulate OP_EQUALVERIFY must not evaluate a transaction to invalid with two of the same pubkeys" in {
val stack = List(pubKeyHash, pubKeyHash)
val script = List(OP_EQUALVERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opEqualVerify(program)
result.isValid must be (true)
//if verification fails it will transform the script to a ExecutedProgram with an error set
result.isInstanceOf[ExecutedScriptProgram] must be (false)
}
it must "evaluate OP_EQUALVERIFY to false given two different pub keys" in {
val uniquePubKey = ScriptConstantImpl(pubKeyHash.hex +"00")
val stack = List(pubKeyHash,uniquePubKey)
val script = List(OP_EQUALVERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opEqualVerify(program)
result.isValid must be (false)
result.stackTopIsTrue must be (false)
}
@ -68,7 +69,7 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte
val stack1 = List( ScriptConstantImpl("02"),ScriptNumberFactory.fromNumber(2))
val script1 = List(OP_EQUAL)
val program1 = ScriptProgram(TestUtil.testProgram, stack,script)
val program1 = ScriptProgram(TestUtil.testProgram, stack1,script1)
opEqual(program1).stack.head must be (ScriptTrue)
}

View file

@ -2,7 +2,8 @@ package org.scalacoin.script.constant
import org.scalacoin.script.ScriptProgram
import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.util.TestUtil
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
@ -52,7 +53,7 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.fromNumber(0).get)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opPushData1(program)
newProgram.isValid must be (true)
newProgram.stackTopIsFalse must be (true)
newProgram.stack must be (List(ScriptNumberFactory.zero))
val stack1 = List()
@ -72,8 +73,8 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
it must "mark a program as invalid if we have do not have enough bytes to be pushed onto the stack by the push operation" in {
val stack = List()
val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.factory(1).get)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opPushData1(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opPushData1(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
}

View file

@ -1,12 +1,13 @@
package org.scalacoin.script.control
import org.scalacoin.marshallers.script.ScriptParser
import org.scalacoin.script.error.{ScriptErrorOpReturn, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.arithmetic.OP_ADD
import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant._
import org.scalacoin.script.reserved.{OP_VER, OP_RESERVED}
import org.scalacoin.util.{TestUtil, Empty, Node, Leaf}
import org.scalacoin.util._
import org.scalatest.{MustMatchers, FlatSpec}
/**
@ -21,7 +22,6 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val result = opVerify(program)
result.stack.isEmpty must be (true)
result.script.isEmpty must be (true)
result.isValid must be (true)
}
it must "have OP_VERIFY evaluate to true when there are multiple items on the stack that can be cast to an int" in {
@ -31,24 +31,23 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val result = opVerify(program)
result.isValid must be (true)
}
it must "have OP_VERIFY evaluate to false with '0' on the stack" in {
val stack = List(ScriptFalse)
val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opVerify(program)
result.isValid must be (false)
result.stackTopIsFalse must be (true)
}
it must "mark the script as invalid for OP_VERIFY when there is nothing on the stack" in {
val stack = List()
val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val result = opVerify(program)
result.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = ScriptProgramTestUtil.toExecutedScriptProgram(opVerify(program))
result.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -468,10 +467,10 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
it must "mark a transaction as invalid if it is trying to spend an OP_RETURN output" in {
val stack = Seq()
val script = Seq(OP_RETURN)
val program = ScriptProgram(TestUtil.testProgram,stack,script)
val newProgram = opReturn(program)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opReturn(program))
newProgram.isValid must be (false)
newProgram.error must be (Some(ScriptErrorOpReturn))
}

File diff suppressed because one or more lines are too long

View file

@ -1,9 +1,10 @@
package org.scalacoin.script.locktime
import org.scalacoin.protocol.transaction.{TransactionInput, Transaction, UpdateTransactionInputs}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant.{ScriptNumberImpl, OP_0}
import org.scalacoin.util.TestUtil
import org.scalacoin.script.error.{ScriptErrorNegativeLockTime, ScriptErrorUnsatisfiedLocktime, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ExecutionInProgressScriptProgram, ExecutedScriptProgram, PreExecutionScriptProgram, ScriptProgram}
import org.scalacoin.script.constant.{ScriptNumberFactory, ScriptNumberImpl, OP_0}
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
import org.scalatest.{MustMatchers, FlatSpec}
/**
@ -14,60 +15,60 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
"LockTimeInterpreter" must "mark the transaction invalid if the stack is empty" in {
val stack = Seq()
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val program = ScriptProgram(TestUtil.testProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "mark the transaction invalid if the transaction's sequence number is set to the max" in {
val stack = Seq(OP_0)
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val program = ScriptProgram(TestUtil.testProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
}
it must "mark the transaction as invalid if the stack top is negative" in {
val stack = Seq(ScriptNumberImpl(-1))
val stack = Seq(ScriptNumberFactory.fromNumber(-1))
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (false)
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.error must be (Some(ScriptErrorNegativeLockTime))
}
it must "mark the transaction as invalid if the locktime on the tx is < 500000000 && stack top is >= 500000000" in {
val stack = Seq(ScriptNumberImpl(500000000))
val stack = Seq(ScriptNumberFactory.fromNumber(500000000))
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (false)
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
}
it must "mark the transaction as invalid if the locktime on the tx is >= 500000000 && stack top is < 500000000" in {
val stack = Seq(ScriptNumberImpl(499999999))
val stack = Seq(ScriptNumberFactory.fromNumber(499999999))
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (false)
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
}
it must "mark the transaction as valid if the locktime on the tx is < 500000000 && stack top is < 500000000" in {
val stack = Seq(ScriptNumberImpl(499999999))
val stack = Seq(ScriptNumberFactory.fromNumber(499999999))
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
@ -76,20 +77,24 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (true)
//if an error is hit, the newProgram will be an instance of ExecutedScriptProgram
//if an error is not hit it will still be a ExecutionInProgressScriptProgram
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
}
it must "mark the transaction as valid if the locktime on the tx is >= 500000000 && stack top is >= 500000000" in {
val stack = Seq(ScriptNumberImpl(500000000))
val stack = Seq(ScriptNumberFactory.fromNumber(500000000))
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
val baseProgram : PreExecutionScriptProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
newProgram.isValid must be (true)
//if an error is hit, the newProgram will be an instance of ExecutedScriptProgram
//if an error is not hit it will still be a ExecutionInProgressScriptProgram
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
}
}

View file

@ -1,9 +1,10 @@
package org.scalacoin.script.stack
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSUtil, TestUtil}
import org.scalacoin.util.{ScriptProgramTestUtil, BitcoinSUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
@ -34,8 +35,8 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
it must "mark the script invalid when calling opDup without an element on top of the stack" in {
val stack = List()
val script = List(OP_DUP)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
opDup(program).isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
ScriptProgramTestUtil.toExecutedScriptProgram(opDup(program)).error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -111,9 +112,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0)
val script = List(OP_NIP)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opNip(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -121,9 +122,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List()
val script = List(OP_NIP)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opNip(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "evaluate an OP_OVER correctly" in {
@ -139,9 +140,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0)
val script = List(OP_OVER)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opOver(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -149,9 +150,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List()
val script = List(OP_OVER)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opOver(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -166,10 +167,10 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
newProgram.script.isEmpty must be (true)
}
/* it must "evaluate an OP_ROLL correctly" in {
it must "evaluate an OP_ROLL correctly" in {
val stack = List(OP_0, ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_ROLL)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opRoll(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),
@ -181,7 +182,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
it must "evaluate an OP_ROT correctly" in {
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opRot(program)
newProgram.stack must be (List(ScriptConstantImpl("16"),ScriptConstantImpl("14"),ScriptConstantImpl("15")))
@ -192,9 +193,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"))
val script = List(OP_ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = opRot(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opRot(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -202,7 +203,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Rot(program)
newProgram.stack must be (List(ScriptConstantImpl("18"),ScriptConstantImpl("19"),ScriptConstantImpl("14"),
@ -210,23 +211,21 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
newProgram.script.isEmpty must be (true)
}
it must "throw an exception if there is less than 6 elements on the stack for OP_2ROT" in {
it must "mark a scirpt as invalid if there is less than 6 elements on the stack for OP_2ROT" in {
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"))
val script = List(OP_2ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
intercept[IllegalArgumentException] {
val newProgram = op2Rot(program)
}
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Rot(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
it must "evalauate an OP_2DROP correctly" in {
it must "evaluate an OP_2DROP correctly" in {
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2DROP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Drop(program)
newProgram.stack must be (List(ScriptConstantImpl("16"),
@ -238,7 +237,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opSwap(program)
newProgram.stack must be (List(ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")))
@ -249,7 +248,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_TUCK)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opTuck(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")))
@ -260,9 +259,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0)
val script = List(OP_TUCK)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = opTuck(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opTuck(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -270,7 +269,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Dup(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),
ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"),
@ -282,9 +281,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0)
val script = List(OP_2DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = op2Dup(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Dup(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -292,7 +291,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_3DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op3Dup(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("16"),
@ -305,7 +304,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Over(program)
newProgram.stack must be (List(ScriptConstantImpl("16"),
@ -318,9 +317,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_2OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = op2Over(program)
newProgram.isValid must be (false)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Over(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
@ -328,7 +327,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Swap(program)
newProgram.stack must be (List(ScriptConstantImpl("16"), ScriptConstantImpl("17"),ScriptConstantImpl("14"),
@ -340,8 +339,8 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_2SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = op2Swap(program)
newProgram.isValid must be (false)
}*/
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Swap(program))
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
}
}

View file

@ -0,0 +1,47 @@
package org.scalacoin.util
import org.scalacoin.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
/**
* Created by chris on 4/20/16.
*/
trait ScriptProgramTestUtil {
/**
* Matches a ScriptProgram to an ExecutedScriptProgram or else throws an exception
* useful for testing purposes
* @param p
* @return
*/
def toExecutedScriptProgram(p : ScriptProgram) : ExecutedScriptProgram = p match {
case e : ExecutedScriptProgram => e
case _ : PreExecutionScriptProgram | _ : ExecutionInProgressScriptProgram =>
throw new RuntimeException("Should be a executed script proram")
}
/**
* Matches a ScriptProgram to a PreExecutionScriptProgram or else throws an exception
* useful to for test purposes
* @param p
* @return
*/
def toPreExecutionScriptProgram(p : ScriptProgram) : PreExecutionScriptProgram = p match {
case e : PreExecutionScriptProgram => e
case _ : ExecutionInProgressScriptProgram | _ : ExecutedScriptProgram =>
throw new RuntimeException("Must be a pre executed scirpt program")
}
/**
* Matches a ScriptProgram to a ExecutionInProgressScriptProgram or else throws an exception
* @param p
* @return
*/
def toExecutionInProgressScriptProgram(p : ScriptProgram) : ExecutionInProgressScriptProgram = p match {
case e : ExecutionInProgressScriptProgram => e
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
throw new RuntimeException("Must be a execution in progress script program")
}
}
object ScriptProgramTestUtil extends ScriptProgramTestUtil

View file

@ -6,7 +6,7 @@ import org.scalacoin.policy.Policy
import org.scalacoin.protocol.script._
import org.scalacoin.protocol.transaction.{EmptyTransaction, Transaction}
import org.scalacoin.protocol.{AssetAddress, BitcoinAddress}
import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.scalacoin.script.constant._
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160}
@ -142,6 +142,14 @@ object TestUtil {
def testProgram : ScriptProgram = ScriptProgram(TransactionTestUtil.testTransaction,
EmptyScriptPubKey,0,List(),Policy.standardScriptVerifyFlags)
def testProgramPreExecution = testProgram match {
case p : PreExecutionScriptProgram => p
case _ => throw new RuntimeException("this must be a script program that is pre execution")
}
def testProgramExecutionInProgress = ScriptProgram.toExecutionInProgress(testProgramPreExecution)
val rawP2PKScriptSig = "47304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001"
def p2pkScriptSig = ScriptSignature(rawP2PKScriptSig)
@ -161,4 +169,9 @@ object TestUtil {
ScriptConstantImpl("173014020002107777777777777777777777777777777701"),
BytesToPushOntoStackImpl(33),
ScriptConstantImpl("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652")))
}