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 SignatureValidationFailureIncorrectSignatures
} }
else if (requiredSigs > sigs.size) { 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") 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) { else if (!sigs.isEmpty && !pubKeys.isEmpty) {
val sig = sigs.head val sig = sigs.head
@ -91,6 +93,8 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
multiSignatureEvaluator(txSignatureComponent, sigs,pubKeys.tail,flags, requiredSigs) multiSignatureEvaluator(txSignatureComponent, sigs,pubKeys.tail,flags, requiredSigs)
case SignatureValidationFailureNotStrictDerEncoding => case SignatureValidationFailureNotStrictDerEncoding =>
SignatureValidationFailureNotStrictDerEncoding SignatureValidationFailureNotStrictDerEncoding
case SignatureValidationFailureSignatureCount =>
SignatureValidationFailureSignatureCount
} }
} else if (sigs.isEmpty) { } else if (sigs.isEmpty) {
//means that we have checked all of the sigs against the public keys //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 { case object SignatureValidationFailureIncorrectSignatures extends TransactionSignatureCheckerResult {
def isValid = false 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.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.protocol.transaction.Transaction import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.script.error.ScriptError
import org.scalacoin.script.flag.ScriptFlag import org.scalacoin.script.flag.ScriptFlag
import org.scalacoin.util.Factory import org.scalacoin.util.Factory
/** /**
* Created by chris on 2/3/16. * Created by chris on 2/3/16.
*/ */
trait ScriptProgram { sealed trait ScriptProgram {
/** /**
@ -59,20 +60,7 @@ trait ScriptProgram {
def flags : Seq[ScriptFlag] 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 * Returns if the stack top is true
* *
* @return * @return
@ -91,14 +79,77 @@ trait ScriptProgram {
else if (!stack.headOption.isDefined) true else if (!stack.headOption.isDefined) true
else false 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 { 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], * Implementation type for a script program that is currently being executed by the script interpreter
flags : Seq[ScriptFlag], isValid : Boolean = true, lastCodeSeparator : Int = 0) extends ScriptProgram * @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 //indicates whether the script or the stack needs to be updated
@ -107,37 +158,45 @@ object ScriptProgram {
case object Script extends UpdateIndicator case object Script extends UpdateIndicator
case object AltStack extends UpdateIndicator case object AltStack extends UpdateIndicator
/** /**
* Changes the validity of a script program * Sets an error on the script program
* * @param oldProgram the program who has hit an invalid state
* @param oldProgram * @param error the error that thet program hit while being executed in the script interpreter
* @param valid * @return the ExecutedScriptProgram with the given error set inside of the trait
* @return */
*/ def factory(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = oldProgram match {
def factory(oldProgram : ScriptProgram, valid : Boolean) : ScriptProgram = { case program : PreExecutionScriptProgram =>
ScriptProgramImpl(oldProgram.txSignatureComponent, throw new RuntimeException("We cannot set an error on the script program before it is executed")
oldProgram.stack,oldProgram.script, oldProgram.originalScript, case program : ExecutionInProgressScriptProgram =>
oldProgram.altStack, oldProgram.flags, valid, oldProgram.lastCodeSeparator) 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 * Updates the program script verify flags
*
* @param oldProgram * @param oldProgram
* @param flags * @param flags
* @return * @return
*/ */
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = { def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = oldProgram match {
ScriptProgramImpl(oldProgram.txSignatureComponent, case program : PreExecutionScriptProgram =>
oldProgram.stack,oldProgram.script, oldProgram.originalScript, PreExecutionScriptProgramImpl(program.txSignatureComponent,program.stack,program.script,program.originalScript,
oldProgram.altStack, flags, oldProgram.isValid, oldProgram.lastCodeSeparator) 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 oldProgram
* @param tokens * @param tokens
* @param indicator * @param indicator
@ -145,115 +204,103 @@ object ScriptProgram {
*/ */
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = { def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
indicator match { indicator match {
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent,tokens.toList, oldProgram.script, case Stack =>
oldProgram.originalScript,oldProgram.altStack, oldProgram.flags, oldProgram match {
oldProgram.isValid, oldProgram.lastCodeSeparator) case program : PreExecutionScriptProgram =>
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent, PreExecutionScriptProgramImpl(program.txSignatureComponent, tokens.toList,program.script,program.originalScript,
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags, program.altStack,program.flags)
oldProgram.isValid, oldProgram.lastCodeSeparator)
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent, case program : ExecutionInProgressScriptProgram =>
oldProgram.stack, oldProgram.script, oldProgram.originalScript,tokens.toList,oldProgram.flags, ExecutionInProgressScriptProgramImpl(program.txSignatureComponent,tokens.toList,program.script,program.originalScript,
oldProgram.isValid, oldProgram.lastCodeSeparator) 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 * Changes the stack tokens and script tokens in a ScriptProgram
*
* @param oldProgram * @param oldProgram
* @param stackTokens * @param stackTokens
* @param scriptTokens * @param scriptTokens
* @return * @return
*/ */
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = { def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = {
val updatedStack = factory(oldProgram,stackTokens,Stack) val updatedStack = apply(oldProgram,stackTokens,Stack)
val updatedScript = factory(updatedStack,scriptTokens,Script) val updatedScript = apply(updatedStack,scriptTokens,Script)
updatedScript 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 * Updates the last OP_CODESEPARATOR index
*
* @param oldProgram * @param oldProgram
* @param lastCodeSeparator * @param lastCodeSeparator
* @return * @return
*/ */
def factory(oldProgram : ScriptProgram, lastCodeSeparator : Int) : ScriptProgram = { def factory(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
ScriptProgramImpl(oldProgram.txSignatureComponent, ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript, 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 * Updates the tokens in either the stack or script and the last OP_CODESEPARATOR index
*
* @param oldProgram * @param oldProgram
* @param tokens * @param tokens
* @param indicator * @param indicator
* @param lastCodeSeparator * @param lastCodeSeparator
* @return * @return
*/ */
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, def factory(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ScriptProgram = { lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
indicator match { val updatedIndicator = apply(oldProgram, tokens, indicator)
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent, updatedIndicator match {
tokens.toList, oldProgram.script, oldProgram.originalScript, oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator) case e : ExecutionInProgressScriptProgram =>
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent, apply(e,lastCodeSeparator)
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator) case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent, throw new RuntimeException("We must have a ExecutionInProgressScriptProgram to update the last OP_CODESEPARATOR index")
oldProgram.stack, oldProgram.script,oldProgram.originalScript,
tokens.toList, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
} }
} }
/** /**
* Updates the stack, script and validity of a script program * Updates the stack, script, alt stack of the given oldProgram
*
* @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
*
* @param oldProgram * @param oldProgram
* @param stack * @param stack
* @param script * @param script
* @param altStack * @param altStack
* @param updateIndicator
* @return * @return
*/ */
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken], def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken]) : ScriptProgram = {
updateIndicator: UpdateIndicator) : ScriptProgram = { val updatedProgramStack = apply(oldProgram,stack, Stack)
ScriptProgramImpl(oldProgram.txSignatureComponent, val updatedProgramScript = apply(updatedProgramStack, script, Script)
stack.toList,script.toList,oldProgram.originalScript,altStack.toList,oldProgram.flags, oldProgram.isValid,oldProgram.lastCodeSeparator) 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 * 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 * 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 * @return the script program representing all of this information
*/ */
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int,
flags : Seq[ScriptFlag]) : ScriptProgram = { flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex).scriptSignature.asm 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 * @return the script program representing all of this information
*/ */
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, script : Seq[ScriptToken], 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) 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 * 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 * is useful for after execution of a scriptSig, copying the stack into this program with the scriptPubKey read to
* run inside the script variable * run inside the script variable
*
* @param transaction the transaction being checked * @param transaction the transaction being checked
* @param scriptPubKey the scriptPubKey which the input is spending * @param scriptPubKey the scriptPubKey which the input is spending
* @param inputIndex the input's index inside of the transaction we are 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], def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = { script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
val program = factory(transaction,scriptPubKey,inputIndex,script,flags) val program = factory(transaction,scriptPubKey,inputIndex,script,flags)
factory(program,stack,Stack) apply(program,stack,Stack)
} }
@ -319,32 +365,79 @@ object ScriptProgram {
* @return * @return
*/ */
def factory(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram = { 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) 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, flags : Seq[ScriptFlag]) : ScriptProgram = factory(oldProgram, flags)
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram =
factory(oldProgram, tokens, indicator) factory(oldProgram, tokens, indicator)
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram =
factory(oldProgram, stackTokens, scriptTokens) 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 : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = factory(oldProgram, lastCodeSeparator)
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, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram =
factory(oldProgram, tokens, indicator, lastCodeSeparator) 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], 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, 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], 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], 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) 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 = def apply(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram =
factory(txSignatureComponent, stack, script) 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 package org.scalacoin.script.arithmetic
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY} import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.scalacoin.script.error.{ScriptErrorUnknownError, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.BitcoinSUtil import org.scalacoin.util.BitcoinSUtil
@ -142,7 +143,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_NUMEQUALVERIFY") "Script top must be OP_NUMEQUALVERIFY")
if (program.stack.size < 2) { if (program.stack.size < 2) {
logger.error("OP_NUMEQUALVERIFY requires two stack elements") logger.error("OP_NUMEQUALVERIFY requires two stack elements")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val numEqualProgram = ScriptProgram(program, program.stack, OP_NUMEQUAL :: program.script.tail) val numEqualProgram = ScriptProgram(program, program.stack, OP_NUMEQUAL :: program.script.tail)
val numEqualResult = opNumEqual(numEqualProgram) val numEqualResult = opNumEqual(numEqualProgram)
@ -225,7 +226,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
if (program.stack.size < 2) { if (program.stack.size < 2) {
logger.error("OP_MIN requires at least two stack elements") logger.error("OP_MIN requires at least two stack elements")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val b = program.stack.head val b = program.stack.head
val a = program.stack.tail.head val a = program.stack.tail.head
@ -252,7 +253,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_MAX") "Script top must be OP_MAX")
if (program.stack.size < 2) { if (program.stack.size < 2) {
logger.error("OP_MAX requires at least two stack elements") logger.error("OP_MAX requires at least two stack elements")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val b = program.stack.head val b = program.stack.head
val a = program.stack.tail.head val a = program.stack.tail.head
@ -281,7 +282,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
"Script top must be OP_WITHIN") "Script top must be OP_WITHIN")
if (program.stack.size < 3) { if (program.stack.size < 3) {
logger.error("OP_WITHIN requires at least 3 elements on the stack") logger.error("OP_WITHIN requires at least 3 elements on the stack")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val c = program.stack.head val c = program.stack.head
val b = program.stack.tail.head val b = program.stack.tail.head
@ -319,7 +320,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
program.stack.headOption match { program.stack.headOption match {
case None => case None =>
logger.error("We need one stack element for performing a unary arithmetic operation") logger.error("We need one stack element for performing a unary arithmetic operation")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Some(s : ScriptNumber) => case Some(s : ScriptNumber) =>
if (checkBitcoinIntByteSize(s)) { if (checkBitcoinIntByteSize(s)) {
val newScriptNumber = op(s) val newScriptNumber = op(s)
@ -327,15 +328,19 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
} }
else { else {
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s) 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) => case Some(s : ScriptConstant) =>
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex)) val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex))
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack) val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
performUnaryArithmeticOperation(newProgram, op) performUnaryArithmeticOperation(newProgram, op)
case Some(s : ScriptToken) => 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") 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 = { private def performBinaryArithmeticOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = {
if (program.stack.size < 2) { if (program.stack.size < 2) {
logger.error("We must have two elements to perform a binary arithmetic operation") logger.error("We must have two elements to perform a binary arithmetic operation")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
(program.stack.head, program.stack.tail.head) match { (program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) => case (x : ScriptNumber, y : ScriptNumber) =>
@ -357,8 +362,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail) ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
} }
else { 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) 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) => case (x : ScriptConstant, y : ScriptNumber) =>
//interpret x as a number //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) val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
performBinaryArithmeticOperation(newProgram, op) performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) => 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") 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) { if (program.stack.size < 2) {
logger.error("We need two stack elements for a binary boolean operation") logger.error("We need two stack elements for a binary boolean operation")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
(program.stack.head, program.stack.tail.head) match { (program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) => case (x : ScriptNumber, y : ScriptNumber) =>
@ -404,8 +413,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail) ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
} }
else { 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) 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) => case (x : ScriptConstant, y : ScriptNumber) =>
//interpret x as a number //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) val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
performBinaryBooleanOperation(newProgram, op) performBinaryBooleanOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) => 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") 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 package org.scalacoin.script.bitwise
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.script.control.{OP_VERIFY, ControlOperationsInterpreter} 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") require(program.script.headOption.isDefined && program.script.head == OP_EQUAL, "Script operation must be OP_EQUAL")
if (program.stack.size < 2) { if (program.stack.size < 2) {
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val h = program.stack.head val h = program.stack.head
val h1 = program.stack.tail.head val h1 = program.stack.tail.head
@ -65,7 +66,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
opVerify(newProgram) opVerify(newProgram)
case false => case false =>
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack") 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 package org.scalacoin.script.constant
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil} import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
import org.slf4j.LoggerFactory 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 //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 we do not, mark the program as invalid
if (bytesNeeded == 0) ScriptProgram(program, ScriptNumberFactory.zero :: program.stack, newScript) 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) else ScriptProgram(program, constant :: program.stack, newScript)
} }

View file

@ -1,5 +1,6 @@
package org.scalacoin.script.control package org.scalacoin.script.control
import org.scalacoin.script.error.{ScriptErrorVerify, ScriptErrorOpReturn, ScriptErrorInvalidStackOperation, ScriptErrorUnbalancedConditional}
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util._ import org.scalacoin.util._
@ -25,10 +26,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
logger.debug("Parsed binary tree: " + binaryTree) logger.debug("Parsed binary tree: " + binaryTree)
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) { if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
logger.error("We do not have a matching OP_ENDIF for every OP_IF we have") 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) { } else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_IF") logger.error("We do not have any stack elements for our OP_IF")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} }
else if (program.stackTopIsTrue) { else if (program.stackTopIsTrue) {
logger.debug("OP_IF stack top was true") logger.debug("OP_IF stack top was true")
@ -60,10 +61,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) { if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
logger.error("We do not have a matching OP_ENDIF for every OP_NOTIF we have") 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) { } else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_NOTIF") logger.error("We do not have any stack elements for our OP_NOTIF")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else if (program.stackTopIsTrue) { } else if (program.stackTopIsTrue) {
//remove the OP_NOTIF //remove the OP_NOTIF
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree) val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
@ -114,7 +115,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) { if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
//means we do not have a matching OP_IF for our OP_ENDIF //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") 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) } else ScriptProgram(program, program.stack,program.script.tail)
} }
@ -130,7 +131,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
*/ */
def opReturn(program : ScriptProgram) : ScriptProgram = { def opReturn(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_RETURN) 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 = { def opVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY") 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 => case true =>
logger.debug("Stack for OP_VERIFY: " + program.stack) 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) else ScriptProgram(program, program.stack.tail,program.script.tail)
case false => case false =>
logger.error("OP_VERIFY requires an element to be on the stack") 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.script._
import org.scalacoin.protocol.transaction.Transaction import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY} 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.flag.{ScriptVerifyNullDummy, ScriptVerifyDerSig}
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ExecutionInProgressScriptProgram, ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil} import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
import org.slf4j.LoggerFactory 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") require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
if (program.stack.size < 2) { if (program.stack.size < 2) {
logger.error("OP_CHECKSIG requires at lest two stack elements") logger.error("OP_CHECKSIG requires at lest two stack elements")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
val pubKey = ECFactory.publicKey(program.stack.head.bytes) val pubKey = ECFactory.publicKey(program.stack.head.bytes)
val signature = ECFactory.digitalSignature(program.stack.tail.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 //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" + 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) "Sig: " + signature.hex)
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorSigDer)
} else { } else {
val restOfStack = program.stack.tail.tail val restOfStack = program.stack.tail.tail
@ -103,10 +104,11 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
case SignatureValidationSuccess => ScriptProgram(program, case SignatureValidationSuccess => ScriptProgram(program,
ScriptTrue :: restOfStack,program.script.tail) ScriptTrue :: restOfStack,program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding => case SignatureValidationFailureNotStrictDerEncoding =>
ScriptProgram(program, ScriptFalse :: restOfStack, ScriptProgram(program, ScriptErrorSigDer)
program.script.tail,SignatureValidationFailureNotStrictDerEncoding.isValid)
case SignatureValidationFailureIncorrectSignatures => case SignatureValidationFailureIncorrectSignatures =>
ScriptProgram(program, ScriptFalse :: restOfStack,program.script.tail) 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) val indexOfOpCodeSeparator = fullScript.indexOf(OP_CODESEPARATOR)
require(indexOfOpCodeSeparator != -1,"The script we searched MUST contain an OP_CODESEPARTOR. Script: " + fullScript) 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) { 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") 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) { } else if (program.stack.size < 3) {
logger.error("OP_CHECKMULTISIG requires at least 3 stack elements") logger.error("OP_CHECKMULTISIG requires at least 3 stack elements")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else { } else {
//these next lines remove the appropriate stack/script values after the signatures have been checked //these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : Int = program.stack.head match { 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 //set the valid flag to false on the script
//see BIP66 for more information on this //see BIP66 for more information on this
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification //https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgram(program, restOfStack, program.script.tail,false) ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures => case SignatureValidationFailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly //this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack //just push a ScriptFalse onto the stack
ScriptProgram(program, ScriptFalse :: restOfStack, program.script.tail) 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 = { def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY") 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") if (program.stack.size < 3) {
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail logger.error("Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script) ScriptProgram(program,ScriptErrorInvalidStackOperation)
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram) } else {
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack) val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
val programFromOpVerify = opVerify(programFromOpCheckMultiSig) val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
programFromOpVerify 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) ScriptProgram(program, hash :: program.stack.tail, program.script.tail)
} else { } else {
logger.error("We must have the stack top defined to execute a hash function") 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.script._
import org.scalacoin.protocol.transaction.Transaction import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.error._
import org.scalacoin.script.flag._ import org.scalacoin.script.flag._
import org.scalacoin.script.locktime.{OP_CHECKLOCKTIMEVERIFY, LockTimeInterpreter} import org.scalacoin.script.locktime.{OP_CHECKLOCKTIMEVERIFY, LockTimeInterpreter}
import org.scalacoin.script.splice._ 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.arithmetic._
import org.scalacoin.script.bitwise._ import org.scalacoin.script.bitwise._
import org.scalacoin.script.constant._ 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 * @return program the final state of the program after being evaluated by the interpreter
*/ */
@tailrec @tailrec
def loop(program : ScriptProgram) : (Boolean,ScriptProgram) = { def loop(program : ScriptProgram) : (Boolean,ExecutedScriptProgram) = {
logger.debug("Stack: " + program.stack) logger.debug("Stack: " + program.stack)
logger.debug("Script: " + program.script) 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("We have reached the maximum amount of script operations allowed")
logger.error("Here are the remaining operations in the script: " + program.script) logger.error("Here are the remaining operations in the script: " + program.script)
(false,ScriptProgram(program,false)) loop(ScriptProgram(program,ScriptErrorOpCount))
} else if (program.script.flatMap(_.bytes).size > 10000) { } 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") 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 { } else {
program.script match { program match {
//if at any time we see that the program is not valid case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)))
//cease script execution case p : ExecutedScriptProgram =>
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 =>
//reset opCount variable to zero since we may need to count the ops //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 //in the scriptPubKey - we don't want the op count of the scriptSig
//to count towards the scriptPubKey op count //to count towards the scriptPubKey op count
opCount = 0 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 { 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 //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) val stack = BitcoinScriptUtil.filterPushOps(scriptSig.scriptSignatureNoRedeemScript.asm.reverse)
logger.debug("P2sh stack: " + stack) logger.debug("P2sh stack: " + stack)
logger.debug("P2sh redeemScript: " + scriptSig.redeemScript.asm) 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) loop(p2shRedeemScriptProgram)
case false => case false =>
logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript") 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 | case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : MultiSignatureScriptSignature |
_ : NonStandardScriptSignature | _ : P2SHScriptSignature | EmptyScriptSignature => _ : NonStandardScriptSignature | _ : P2SHScriptSignature | EmptyScriptSignature =>
val scriptSigProgram = ScriptProgram(program,Seq(),program.txSignatureComponent.scriptSignature.asm)
val (scriptSigProgramIsValid,scriptSigExecutedProgram) = loop(scriptSigProgram) val (scriptSigProgramIsValid,scriptSigExecutedProgram) = loop(scriptSigProgram)
logger.info("Stack state after scriptSig execution: " + scriptSigExecutedProgram.stack) logger.info("Stack state after scriptSig execution: " + scriptSigExecutedProgram.stack)
logger.info("scriptSigExecutedProgram: " + scriptSigExecutedProgram.error)
logger.info("scriptSigProgramIsValid: " + scriptSigProgramIsValid)
if (scriptSigProgramIsValid) { if (scriptSigProgramIsValid) {
logger.debug("We do not check a redeemScript against a non p2sh scriptSig") 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 //now run the scriptPubKey script through the interpreter with the scriptSig as the stack arguments
val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent, val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent,
scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm) scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
require(scriptPubKeyProgram.script == scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
val (scriptPubKeyProgramIsValid, scriptPubKeyExecutedProgram) = loop(scriptPubKeyProgram) val (scriptPubKeyProgramIsValid, scriptPubKeyExecutedProgram) = loop(scriptPubKeyProgram)
logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack) logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack)
//if the program is valid, return if the stack top is true //if the program is valid, return if the stack top is true
//else the program is false since something illegal happened during script evaluation //else the program is false since something illegal happened during script evaluation
scriptPubKeyProgramIsValid match { scriptPubKeyProgramIsValid match {

View file

@ -1,7 +1,8 @@
package org.scalacoin.script.locktime package org.scalacoin.script.locktime
import org.scalacoin.protocol.transaction.TransactionConstants 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.script.{ScriptProgram}
import org.scalacoin.util.BitcoinSLogger import org.scalacoin.util.BitcoinSLogger
@ -28,25 +29,26 @@ trait LockTimeInterpreter extends BitcoinSLogger {
"Script top must be OP_CHECKLOCKTIMEVERIFY") "Script top must be OP_CHECKLOCKTIMEVERIFY")
if (program.stack.size == 0) { if (program.stack.size == 0) {
logger.warn("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because we have no stack items") 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) { } 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") 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 { else {
val isValid = program.stack.head match { val isError : Option[ScriptError] = program.stack.head match {
case s : ScriptNumber if (s < ScriptNumberFactory.zero) => case s : ScriptNumber if (s < ScriptNumberFactory.zero) =>
logger.warn("OP_CHECKLOCKTIMEVERIFY marks tx as invalid if the stack top is negative") 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) => 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") 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) => 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") logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
false Some(ScriptErrorUnsatisfiedLocktime)
case _ => true 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 package org.scalacoin.script.splice
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptOperationFactory, ScriptProgram} import org.scalacoin.script.{ScriptOperationFactory, ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import Math._ import Math._
@ -32,7 +33,7 @@ trait SpliceInterpreter extends BitcoinSLogger {
} }
case false => case false =>
logger.error("Must have at least 1 element on the stack for OP_SIZE") 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 package org.scalacoin.script.stack
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil} 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 h :: t => ScriptProgram(program, h :: program.stack, program.script.tail)
case Nil => case Nil =>
logger.error("Cannot duplicate the top element on an empty stack") 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) program.script.tail)
case false => case false =>
logger.error("Cannot duplicate the top element on an empty stack") 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) program.script.tail, program.stack.head :: program.altStack, ScriptProgram.AltStack)
case false => case false =>
logger.error("OP_TOALTSTACK requires an element to be on the stack") 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) program.script.tail, program.altStack.tail, ScriptProgram.AltStack)
case false => case false =>
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK") 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 true => ScriptProgram(program, program.stack.tail,program.script.tail)
case false => case false =>
logger.error("Stack must have at least one item on it for OP_DROP") 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 => ScriptProgram(program, h :: t, program.script.tail)
case h :: t => case h :: t =>
logger.error("Stack must have at least two items on it for OP_NIP") logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgram(program,false) ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Nil => case Nil =>
logger.error("Stack must have at least two items on it for OP_NIP") 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 { program.stack match {
case _ :: h1 :: _ => ScriptProgram(program, h1 :: program.stack, program.script.tail) 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") 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 => case Nil =>
logger.error("Stack must have at least two items on it for OP_OVER") 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) ScriptProgram(program,newStackTop :: program.stack.tail, program.script.tail)
case false => case false =>
logger.error("The index for OP_PICK would have caused an index out of bounds exception") 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) ScriptProgram(program,newStack,program.script.tail)
case false => case false =>
logger.error("The index for OP_ROLL would have caused an index out of bounds exception") 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) ScriptProgram(program, newStack,program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 3 items on it for OP_ROT") 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) ScriptProgram(program, newStack,program.script.tail)
case _ => case _ =>
logger.error("OP_2ROT requires 6 elements on the stack") 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) ScriptProgram(program, program.stack.tail.tail, program.script.tail)
case false => case false =>
logger.error("OP_2DROP requires two elements to be on the stack") 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) ScriptProgram(program, newStack, program.script.tail)
case false => case false =>
logger.error("Stack must have at least 2 items on it for OP_SWAP") 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) ScriptProgram(program, newStack, program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 2 items on it for OP_TUCK") 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) ScriptProgram(program, newStack, program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 2 items on it for OP_2DUP") 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) ScriptProgram(program,newStack,program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 3 items on it for OP_3DUP") 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) ScriptProgram(program, newStack,program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 4 items on it for OP_2OVER") 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) ScriptProgram(program,newStack,program.script.tail)
case _ => case _ =>
logger.error("Stack must have at least 4 items on it for OP_2SWAP") 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 package org.scalacoin.script
import org.scalacoin.script.constant.{OP_1, OP_0} import org.scalacoin.script.constant.{OP_1, OP_0}
import org.scalacoin.script.flag.ScriptFlagFactory
import org.scalacoin.util.TestUtil import org.scalacoin.util.TestUtil
import org.scalatest.{MustMatchers, FlatSpec} import org.scalatest.{MustMatchers, FlatSpec}
@ -24,14 +25,14 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
it must "update the OP_CODESEPARATOR index" in { it must "update the OP_CODESEPARATOR index" in {
val index = 999 val index = 999
val program = ScriptProgram(TestUtil.testProgram,index) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,index)
program.lastCodeSeparator must be (999) program.lastCodeSeparator must be (999)
} }
it must "update the OP_CODESEPARATOR index & stack simultaneously" in { it must "update the OP_CODESEPARATOR index & stack simultaneously" in {
val index = 999 val index = 999
val stack = Seq(OP_0) 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.stack must be (stack)
program.lastCodeSeparator must be (index) 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 { it must "update the OP_CODESEPARATOR index & altStack simultaneously" in {
val index = 999 val index = 999
val altStack = Seq(OP_0) 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.altStack must be (altStack)
program.lastCodeSeparator must be (index) 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 package org.scalacoin.script.arithmetic
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.TestUtil import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers} import org.scalatest.{FlatSpec, MustMatchers}
/** /**
@ -34,9 +35,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val stack = List() val stack = List()
val script = List(OP_1ADD) val script = List(OP_1ADD)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = op1Add(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Add(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
@ -54,9 +55,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
val stack = List() val stack = List()
val script = List(OP_1SUB) val script = List(OP_1SUB)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = op1Sub(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Sub(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "perform an OP_SUB corectly" in { 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 program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opSub(program) 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) 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 { it must "mark a script as invalid if we have an OP_SUB with nothing on the stack" in {
val stack = List() val stack = List()
val script = List(OP_SUB) val script = List(OP_SUB)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opSub(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opSub(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "perform an OP_ABS on a negative number corectly" in { 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 { it must "mark a script as invalid if we have an OP_ABS with nothing on the stack" in {
val stack = List() val stack = List()
val script = List(OP_ABS) val script = List(OP_ABS)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opAbs(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opAbs(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "perform an OP_NEGATE on a zero correctly" in { 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 stack = List()
val script = List(OP_NEGATE) val script = List(OP_NEGATE)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opNegate(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNegate(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "perform an OP_NOT correctly where 0 is the stack top" in { 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.stack.head must be (OP_FALSE)
newProgram1.script.isEmpty must be (true) 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 script2 = List(OP_LESSTHAN)
val program2 = ScriptProgram(TestUtil.testProgram, stack2,script2) val program2 = ScriptProgram(TestUtil.testProgram, stack2,script2)
val newProgram2 = opLessThan(program2) val newProgram2 = opLessThan(program2)

View file

@ -1,6 +1,6 @@
package org.scalacoin.script.bitwise 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.arithmetic.OP_NUMEQUAL
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.TestUtil 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 stack = List(pubKeyHash, pubKeyHash)
val script = List(OP_EQUALVERIFY) val script = List(OP_EQUALVERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opEqualVerify(program) 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 { it must "evaluate OP_EQUALVERIFY to false given two different pub keys" in {
val uniquePubKey = ScriptConstantImpl(pubKeyHash.hex +"00") val uniquePubKey = ScriptConstantImpl(pubKeyHash.hex +"00")
val stack = List(pubKeyHash,uniquePubKey) val stack = List(pubKeyHash,uniquePubKey)
val script = List(OP_EQUALVERIFY) val script = List(OP_EQUALVERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opEqualVerify(program) 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 stack1 = List( ScriptConstantImpl("02"),ScriptNumberFactory.fromNumber(2))
val script1 = List(OP_EQUAL) 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) 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.ScriptProgram
import org.scalacoin.script.bitwise.OP_EQUAL 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} 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 script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.fromNumber(0).get)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opPushData1(program) val newProgram = opPushData1(program)
newProgram.isValid must be (true) newProgram.stackTopIsFalse must be (true)
newProgram.stack must be (List(ScriptNumberFactory.zero)) newProgram.stack must be (List(ScriptNumberFactory.zero))
val stack1 = List() 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 { 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 stack = List()
val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.factory(1).get) val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.factory(1).get)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opPushData1(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opPushData1(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
} }

View file

@ -1,12 +1,13 @@
package org.scalacoin.script.control package org.scalacoin.script.control
import org.scalacoin.marshallers.script.ScriptParser import org.scalacoin.marshallers.script.ScriptParser
import org.scalacoin.script.error.{ScriptErrorOpReturn, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.arithmetic.OP_ADD import org.scalacoin.script.arithmetic.OP_ADD
import org.scalacoin.script.bitwise.OP_EQUAL import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.script.reserved.{OP_VER, OP_RESERVED} 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} import org.scalatest.{MustMatchers, FlatSpec}
/** /**
@ -21,7 +22,6 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val result = opVerify(program) val result = opVerify(program)
result.stack.isEmpty must be (true) result.stack.isEmpty must be (true)
result.script.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 { 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 script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val result = opVerify(program) val result = opVerify(program)
result.isValid must be (true)
} }
it must "have OP_VERIFY evaluate to false with '0' on the stack" in { it must "have OP_VERIFY evaluate to false with '0' on the stack" in {
val stack = List(ScriptFalse) val stack = List(ScriptFalse)
val script = List(OP_VERIFY) val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opVerify(program) 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 { it must "mark the script as invalid for OP_VERIFY when there is nothing on the stack" in {
val stack = List() val stack = List()
val script = List(OP_VERIFY) val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val result = opVerify(program) val result = ScriptProgramTestUtil.toExecutedScriptProgram(opVerify(program))
result.isValid must be (false) 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 { it must "mark a transaction as invalid if it is trying to spend an OP_RETURN output" in {
val stack = Seq() val stack = Seq()
val script = Seq(OP_RETURN) val script = Seq(OP_RETURN)
val program = ScriptProgram(TestUtil.testProgram,stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = opReturn(program) 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 package org.scalacoin.script.locktime
import org.scalacoin.protocol.transaction.{TransactionInput, Transaction, UpdateTransactionInputs} import org.scalacoin.protocol.transaction.{TransactionInput, Transaction, UpdateTransactionInputs}
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.error.{ScriptErrorNegativeLockTime, ScriptErrorUnsatisfiedLocktime, ScriptErrorInvalidStackOperation}
import org.scalacoin.script.constant.{ScriptNumberImpl, OP_0} import org.scalacoin.script.{ExecutionInProgressScriptProgram, ExecutedScriptProgram, PreExecutionScriptProgram, ScriptProgram}
import org.scalacoin.util.TestUtil import org.scalacoin.script.constant.{ScriptNumberFactory, ScriptNumberImpl, OP_0}
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
import org.scalatest.{MustMatchers, FlatSpec} 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 { "LockTimeInterpreter" must "mark the transaction invalid if the stack is empty" in {
val stack = Seq() val stack = Seq()
val script = Seq(OP_CHECKLOCKTIMEVERIFY) val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val program = ScriptProgram(TestUtil.testProgram,stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = opCheckLockTimeVerify(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "mark the transaction invalid if the transaction's sequence number is set to the max" in { it must "mark the transaction invalid if the transaction's sequence number is set to the max" in {
val stack = Seq(OP_0) val stack = Seq(OP_0)
val script = Seq(OP_CHECKLOCKTIMEVERIFY) val script = Seq(OP_CHECKLOCKTIMEVERIFY)
val program = ScriptProgram(TestUtil.testProgram,stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
val newProgram = opCheckLockTimeVerify(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
} }
it must "mark the transaction as invalid if the stack top is negative" in { 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 script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0) val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber))) val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0) val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey, val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags) TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script) val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = opCheckLockTimeVerify(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.isValid must be (false) 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 { 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 script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0) val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber))) val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0) val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey, val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags) TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script) val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = opCheckLockTimeVerify(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.isValid must be (false) 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 { 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 script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0) val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber))) val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000) val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000)
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey, val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags) TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script) val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
val newProgram = opCheckLockTimeVerify(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
newProgram.isValid must be (false) 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 { 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 script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0) val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber))) 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) TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script) val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program) 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 { 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 script = Seq(OP_CHECKLOCKTIMEVERIFY)
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0) val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber))) val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000) 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) TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val program = ScriptProgram(baseProgram,stack,script) val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program) 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 package org.scalacoin.script.stack
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
import org.scalacoin.script.{ScriptProgram} import org.scalacoin.script.{ScriptProgram}
import org.scalacoin.script.bitwise.OP_EQUAL import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSUtil, TestUtil} import org.scalacoin.util.{ScriptProgramTestUtil, BitcoinSUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers} 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 { it must "mark the script invalid when calling opDup without an element on top of the stack" in {
val stack = List() val stack = List()
val script = List(OP_DUP) val script = List(OP_DUP)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
opDup(program).isValid must be (false) 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 stack = List(OP_0)
val script = List(OP_NIP) val script = List(OP_NIP)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opNip(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
@ -121,9 +122,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List() val stack = List()
val script = List(OP_NIP) val script = List(OP_NIP)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opNip(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
it must "evaluate an OP_OVER correctly" in { 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 stack = List(OP_0)
val script = List(OP_OVER) val script = List(OP_OVER)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opOver(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
@ -149,9 +150,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List() val stack = List()
val script = List(OP_OVER) val script = List(OP_OVER)
val program = ScriptProgram(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opOver(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
newProgram.isValid must be (false) newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
} }
@ -166,10 +167,10 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
newProgram.script.isEmpty must be (true) 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 stack = List(OP_0, ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_ROLL) val script = List(OP_ROLL)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opRoll(program) val newProgram = opRoll(program)
newProgram.stack must be (List(ScriptConstantImpl("14"), 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 { it must "evaluate an OP_ROT correctly" in {
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16")) val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_ROT) val script = List(OP_ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opRot(program) val newProgram = opRot(program)
newProgram.stack must be (List(ScriptConstantImpl("16"),ScriptConstantImpl("14"),ScriptConstantImpl("15"))) 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 stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"))
val script = List(OP_ROT) val script = List(OP_ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opRot(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opRot(program))
newProgram.isValid must be (false) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2ROT) val script = List(OP_2ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Rot(program) val newProgram = op2Rot(program)
newProgram.stack must be (List(ScriptConstantImpl("18"),ScriptConstantImpl("19"),ScriptConstantImpl("14"), 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) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18")) ScriptConstantImpl("17"), ScriptConstantImpl("18"))
val script = List(OP_2ROT) val script = List(OP_2ROT)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Rot(program))
intercept[IllegalArgumentException] { newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
val newProgram = op2Rot(program)
}
} }
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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2DROP) val script = List(OP_2DROP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Drop(program) val newProgram = op2Drop(program)
newProgram.stack must be (List(ScriptConstantImpl("16"), 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_SWAP) val script = List(OP_SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opSwap(program) val newProgram = opSwap(program)
newProgram.stack must be (List(ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"), newProgram.stack must be (List(ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_TUCK) val script = List(OP_TUCK)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opTuck(program) val newProgram = opTuck(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"), newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")))
@ -260,9 +259,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0) val stack = List(OP_0)
val script = List(OP_TUCK) val script = List(OP_TUCK)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = opTuck(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opTuck(program))
newProgram.isValid must be (false) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2DUP) val script = List(OP_2DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Dup(program) val newProgram = op2Dup(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"), newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),
ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"), ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"),
@ -282,9 +281,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val stack = List(OP_0) val stack = List(OP_0)
val script = List(OP_2DUP) val script = List(OP_2DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = op2Dup(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Dup(program))
newProgram.isValid must be (false) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_3DUP) val script = List(OP_3DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op3Dup(program) val newProgram = op3Dup(program)
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("16"), 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2OVER) val script = List(OP_2OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Over(program) val newProgram = op2Over(program)
newProgram.stack must be (List(ScriptConstantImpl("16"), 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 stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_2OVER) val script = List(OP_2OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = op2Over(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Over(program))
newProgram.isValid must be (false) 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"), val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
val script = List(OP_2SWAP) val script = List(OP_2SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = op2Swap(program) val newProgram = op2Swap(program)
newProgram.stack must be (List(ScriptConstantImpl("16"), ScriptConstantImpl("17"),ScriptConstantImpl("14"), 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 stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
val script = List(OP_2SWAP) val script = List(OP_2SWAP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
val newProgram = op2Swap(program) val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Swap(program))
newProgram.isValid must be (false) 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.script._
import org.scalacoin.protocol.transaction.{EmptyTransaction, Transaction} import org.scalacoin.protocol.transaction.{EmptyTransaction, Transaction}
import org.scalacoin.protocol.{AssetAddress, BitcoinAddress} 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.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.scalacoin.script.constant._ import org.scalacoin.script.constant._
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160} import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160}
@ -142,6 +142,14 @@ object TestUtil {
def testProgram : ScriptProgram = ScriptProgram(TransactionTestUtil.testTransaction, def testProgram : ScriptProgram = ScriptProgram(TransactionTestUtil.testTransaction,
EmptyScriptPubKey,0,List(),Policy.standardScriptVerifyFlags) 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" val rawP2PKScriptSig = "47304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001"
def p2pkScriptSig = ScriptSignature(rawP2PKScriptSig) def p2pkScriptSig = ScriptSignature(rawP2PKScriptSig)
@ -161,4 +169,9 @@ object TestUtil {
ScriptConstantImpl("173014020002107777777777777777777777777777777701"), ScriptConstantImpl("173014020002107777777777777777777777777777777701"),
BytesToPushOntoStackImpl(33), BytesToPushOntoStackImpl(33),
ScriptConstantImpl("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652"))) ScriptConstantImpl("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652")))
} }