mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 15:02:17 +01:00
Merge pull request #5 from Christewart/script_program_refactoring
Script program refactoring
This commit is contained in:
commit
ee39a2f815
22 changed files with 706 additions and 481 deletions
|
@ -77,8 +77,10 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
SignatureValidationFailureIncorrectSignatures
|
||||
}
|
||||
else if (requiredSigs > sigs.size) {
|
||||
//for the case when we do not have enough sigs left to check to meet the required signature threshold
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915
|
||||
logger.info("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
|
||||
SignatureValidationFailureIncorrectSignatures
|
||||
SignatureValidationFailureSignatureCount
|
||||
}
|
||||
else if (!sigs.isEmpty && !pubKeys.isEmpty) {
|
||||
val sig = sigs.head
|
||||
|
@ -91,6 +93,8 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
|
|||
multiSignatureEvaluator(txSignatureComponent, sigs,pubKeys.tail,flags, requiredSigs)
|
||||
case SignatureValidationFailureNotStrictDerEncoding =>
|
||||
SignatureValidationFailureNotStrictDerEncoding
|
||||
case SignatureValidationFailureSignatureCount =>
|
||||
SignatureValidationFailureSignatureCount
|
||||
}
|
||||
} else if (sigs.isEmpty) {
|
||||
//means that we have checked all of the sigs against the public keys
|
||||
|
|
|
@ -36,3 +36,13 @@ case object SignatureValidationFailureNotStrictDerEncoding extends TransactionSi
|
|||
case object SignatureValidationFailureIncorrectSignatures extends TransactionSignatureCheckerResult {
|
||||
def isValid = false
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This indicates that the signature validation failed because we have more signatures left to check
|
||||
* than public keys remaining to check them against
|
||||
* see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915
|
||||
*/
|
||||
case object SignatureValidationFailureSignatureCount extends TransactionSignatureCheckerResult {
|
||||
def isValid = false
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@ import org.scalacoin.crypto.{TransactionSignatureComponentFactory, TransactionSi
|
|||
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.error.ScriptError
|
||||
import org.scalacoin.script.flag.ScriptFlag
|
||||
import org.scalacoin.util.Factory
|
||||
|
||||
/**
|
||||
* Created by chris on 2/3/16.
|
||||
*/
|
||||
trait ScriptProgram {
|
||||
sealed trait ScriptProgram {
|
||||
|
||||
|
||||
/**
|
||||
|
@ -59,20 +60,7 @@ trait ScriptProgram {
|
|||
def flags : Seq[ScriptFlag]
|
||||
|
||||
/**
|
||||
* A function to determine if the transaction is valid or not
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
def isValid : Boolean
|
||||
|
||||
/**
|
||||
* The index of the last OP_CODESEPARATOR
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
def lastCodeSeparator : Int
|
||||
|
||||
/**
|
||||
* Returns if the stack top is true
|
||||
*
|
||||
* @return
|
||||
|
@ -91,14 +79,77 @@ trait ScriptProgram {
|
|||
else if (!stack.headOption.isDefined) true
|
||||
else false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This represents a ScriptProgram before any script operations have been executed in the interpreter
|
||||
*/
|
||||
sealed trait PreExecutionScriptProgram extends ScriptProgram
|
||||
sealed trait ExecutionInProgressScriptProgram extends ScriptProgram {
|
||||
/**
|
||||
* The index of the last OP_CODESEPARATOR
|
||||
* @return
|
||||
*/
|
||||
def lastCodeSeparator : Int
|
||||
|
||||
}
|
||||
|
||||
sealed trait ExecutedScriptProgram extends ScriptProgram {
|
||||
/**
|
||||
* Indicates if the program has encountered a ScriptError in its execution
|
||||
* @return
|
||||
*/
|
||||
def error : Option[ScriptError]
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory companion object for ScriptProgram
|
||||
*/
|
||||
object ScriptProgram {
|
||||
/**
|
||||
* Implentation type for a script program that has not been executed at all
|
||||
* @param txSignatureComponent
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param originalScript
|
||||
* @param altStack
|
||||
* @param flags
|
||||
*/
|
||||
private sealed case class PreExecutionScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
|
||||
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
|
||||
flags : Seq[ScriptFlag]) extends PreExecutionScriptProgram
|
||||
|
||||
private sealed case class ScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
|
||||
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
|
||||
flags : Seq[ScriptFlag], isValid : Boolean = true, lastCodeSeparator : Int = 0) extends ScriptProgram
|
||||
/**
|
||||
* Implementation type for a script program that is currently being executed by the script interpreter
|
||||
* @param txSignatureComponent
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param originalScript
|
||||
* @param altStack
|
||||
* @param flags
|
||||
* @param lastCodeSeparator
|
||||
*/
|
||||
private sealed case class ExecutionInProgressScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
|
||||
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
|
||||
flags : Seq[ScriptFlag], lastCodeSeparator : Int = 0) extends ExecutionInProgressScriptProgram
|
||||
|
||||
/**
|
||||
* The implementation type for a script program that is finished being executed by the script interpreter
|
||||
* @param txSignatureComponent
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param originalScript
|
||||
* @param altStack
|
||||
* @param flags
|
||||
* @param error
|
||||
*/
|
||||
private sealed case class ExecutedScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
|
||||
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
|
||||
flags : Seq[ScriptFlag], error : Option[ScriptError]) extends ExecutedScriptProgram
|
||||
|
||||
|
||||
//indicates whether the script or the stack needs to be updated
|
||||
|
@ -107,37 +158,45 @@ object ScriptProgram {
|
|||
case object Script extends UpdateIndicator
|
||||
case object AltStack extends UpdateIndicator
|
||||
|
||||
|
||||
/**
|
||||
* Changes the validity of a script program
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param valid
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, valid : Boolean) : ScriptProgram = {
|
||||
ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack,oldProgram.script, oldProgram.originalScript,
|
||||
oldProgram.altStack, oldProgram.flags, valid, oldProgram.lastCodeSeparator)
|
||||
* Sets an error on the script program
|
||||
* @param oldProgram the program who has hit an invalid state
|
||||
* @param error the error that thet program hit while being executed in the script interpreter
|
||||
* @return the ExecutedScriptProgram with the given error set inside of the trait
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = oldProgram match {
|
||||
case program : PreExecutionScriptProgram =>
|
||||
throw new RuntimeException("We cannot set an error on the script program before it is executed")
|
||||
case program : ExecutionInProgressScriptProgram =>
|
||||
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
|
||||
program.altStack, program.flags, Some(error))
|
||||
case program : ExecutedScriptProgram =>
|
||||
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
|
||||
program.altStack, program.flags, Some(error))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the program script verify flags
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = {
|
||||
ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack,oldProgram.script, oldProgram.originalScript,
|
||||
oldProgram.altStack, flags, oldProgram.isValid, oldProgram.lastCodeSeparator)
|
||||
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = oldProgram match {
|
||||
case program : PreExecutionScriptProgram =>
|
||||
PreExecutionScriptProgramImpl(program.txSignatureComponent,program.stack,program.script,program.originalScript,
|
||||
program.altStack,flags)
|
||||
case program : ExecutionInProgressScriptProgram =>
|
||||
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
|
||||
program.altStack, flags, program.lastCodeSeparator)
|
||||
case program : ExecutedScriptProgram =>
|
||||
throw new RuntimeException("Cannot update the script flags on a program that has been executed")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the tokens in either the Stack or the Script dependong in the indicactor
|
||||
*
|
||||
* Changes the tokens in either the Stack or the Script depending in the indicator
|
||||
* @param oldProgram
|
||||
* @param tokens
|
||||
* @param indicator
|
||||
|
@ -145,115 +204,103 @@ object ScriptProgram {
|
|||
*/
|
||||
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
|
||||
indicator match {
|
||||
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent,tokens.toList, oldProgram.script,
|
||||
oldProgram.originalScript,oldProgram.altStack, oldProgram.flags,
|
||||
oldProgram.isValid, oldProgram.lastCodeSeparator)
|
||||
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags,
|
||||
oldProgram.isValid, oldProgram.lastCodeSeparator)
|
||||
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, oldProgram.script, oldProgram.originalScript,tokens.toList,oldProgram.flags,
|
||||
oldProgram.isValid, oldProgram.lastCodeSeparator)
|
||||
case Stack =>
|
||||
oldProgram match {
|
||||
case program : PreExecutionScriptProgram =>
|
||||
PreExecutionScriptProgramImpl(program.txSignatureComponent, tokens.toList,program.script,program.originalScript,
|
||||
program.altStack,program.flags)
|
||||
|
||||
case program : ExecutionInProgressScriptProgram =>
|
||||
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent,tokens.toList,program.script,program.originalScript,
|
||||
program.altStack,program.flags,program.lastCodeSeparator)
|
||||
|
||||
case program : ExecutedScriptProgram =>
|
||||
throw new RuntimeException("Cannot update stack for program that has been fully executed")
|
||||
}
|
||||
case Script =>
|
||||
oldProgram match {
|
||||
case program : PreExecutionScriptProgram =>
|
||||
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,tokens.toList,program.originalScript,
|
||||
program.altStack,program.flags)
|
||||
case program : ExecutionInProgressScriptProgram =>
|
||||
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, tokens.toList, program.originalScript,
|
||||
program.altStack, program.flags)
|
||||
case program : ExecutedScriptProgram =>
|
||||
throw new RuntimeException("Cannot update the script for a program that has been fully executed")
|
||||
}
|
||||
case AltStack => oldProgram match {
|
||||
case program : PreExecutionScriptProgram =>
|
||||
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
|
||||
tokens.toList,program.flags)
|
||||
case program : ExecutionInProgressScriptProgram =>
|
||||
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
|
||||
tokens.toList, program.flags)
|
||||
case program : ExecutedScriptProgram =>
|
||||
throw new RuntimeException("Cannot update the alt stack for a program that has been fully executed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the stack tokens and script tokens in a ScriptProgram
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param stackTokens
|
||||
* @param scriptTokens
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = {
|
||||
val updatedStack = factory(oldProgram,stackTokens,Stack)
|
||||
val updatedScript = factory(updatedStack,scriptTokens,Script)
|
||||
val updatedStack = apply(oldProgram,stackTokens,Stack)
|
||||
val updatedScript = apply(updatedStack,scriptTokens,Script)
|
||||
updatedScript
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the programs stack tokens, script tokens & script verify flags
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param stackTokens
|
||||
* @param scriptTokens
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
|
||||
val updatedStackAndScript = factory(oldProgram,stackTokens,scriptTokens)
|
||||
factory(updatedStackAndScript,flags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the last OP_CODESEPARATOR index
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param lastCodeSeparator
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, lastCodeSeparator : Int) : ScriptProgram = {
|
||||
ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
def factory(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
|
||||
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
|
||||
oldProgram.altStack, oldProgram.flags, isValid = oldProgram.isValid, lastCodeSeparator = lastCodeSeparator)
|
||||
oldProgram.altStack, oldProgram.flags,lastCodeSeparator)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tokens in either the stack or script and the last OP_CODESEPARATOR index
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param tokens
|
||||
* @param indicator
|
||||
* @param lastCodeSeparator
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
|
||||
lastCodeSeparator : Int) : ScriptProgram = {
|
||||
indicator match {
|
||||
case Stack => ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
tokens.toList, oldProgram.script, oldProgram.originalScript, oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
|
||||
case Script => ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, tokens.toList, oldProgram.originalScript,oldProgram.altStack, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
|
||||
case AltStack => ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
oldProgram.stack, oldProgram.script,oldProgram.originalScript,
|
||||
tokens.toList, oldProgram.flags, oldProgram.isValid, lastCodeSeparator)
|
||||
def factory(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
|
||||
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
|
||||
val updatedIndicator = apply(oldProgram, tokens, indicator)
|
||||
updatedIndicator match {
|
||||
case e : ExecutionInProgressScriptProgram =>
|
||||
apply(e,lastCodeSeparator)
|
||||
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
|
||||
throw new RuntimeException("We must have a ExecutionInProgressScriptProgram to update the last OP_CODESEPARATOR index")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the stack, script and validity of a script program
|
||||
*
|
||||
* @param oldProgram
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param valid
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], valid : Boolean) : ScriptProgram = {
|
||||
val stackAndScriptUpdate = factory(oldProgram, stack,script)
|
||||
factory(stackAndScriptUpdate, valid)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the stack, script
|
||||
*
|
||||
* Updates the stack, script, alt stack of the given oldProgram
|
||||
* @param oldProgram
|
||||
* @param stack
|
||||
* @param script
|
||||
* @param altStack
|
||||
* @param updateIndicator
|
||||
* @return
|
||||
*/
|
||||
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
|
||||
updateIndicator: UpdateIndicator) : ScriptProgram = {
|
||||
ScriptProgramImpl(oldProgram.txSignatureComponent,
|
||||
stack.toList,script.toList,oldProgram.originalScript,altStack.toList,oldProgram.flags, oldProgram.isValid,oldProgram.lastCodeSeparator)
|
||||
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken]) : ScriptProgram = {
|
||||
val updatedProgramStack = apply(oldProgram,stack, Stack)
|
||||
val updatedProgramScript = apply(updatedProgramStack, script, Script)
|
||||
val updatedProgramAltStack = apply(updatedProgramScript, altStack, AltStack)
|
||||
updatedProgramAltStack
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new script program that can be used to verify if a transaction at the given inputIndex
|
||||
* spends a given scriptPubKey correctly. Assumes that the script to be executed is the
|
||||
|
@ -266,9 +313,9 @@ object ScriptProgram {
|
|||
* @return the script program representing all of this information
|
||||
*/
|
||||
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int,
|
||||
flags : Seq[ScriptFlag]) : ScriptProgram = {
|
||||
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
|
||||
val script = transaction.inputs(inputIndex).scriptSignature.asm
|
||||
factory(transaction,scriptPubKey,inputIndex,script.toList,flags)
|
||||
apply(transaction,scriptPubKey,inputIndex,script.toList,flags)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,9 +330,9 @@ object ScriptProgram {
|
|||
* @return the script program representing all of this information
|
||||
*/
|
||||
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, script : Seq[ScriptToken],
|
||||
flags : Seq[ScriptFlag]) : ScriptProgram = {
|
||||
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
|
||||
val txSignatureComponent = TransactionSignatureComponentFactory.factory(transaction,inputIndex,scriptPubKey,flags)
|
||||
ScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
|
||||
PreExecutionScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
|
||||
}
|
||||
|
||||
|
||||
|
@ -293,7 +340,6 @@ object ScriptProgram {
|
|||
* The intention for this factory function is to allow us to create a program that already has a stack state. This
|
||||
* is useful for after execution of a scriptSig, copying the stack into this program with the scriptPubKey read to
|
||||
* run inside the script variable
|
||||
*
|
||||
* @param transaction the transaction being checked
|
||||
* @param scriptPubKey the scriptPubKey which the input is spending
|
||||
* @param inputIndex the input's index inside of the transaction we are spending
|
||||
|
@ -304,7 +350,7 @@ object ScriptProgram {
|
|||
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, stack : Seq[ScriptToken],
|
||||
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
|
||||
val program = factory(transaction,scriptPubKey,inputIndex,script,flags)
|
||||
factory(program,stack,Stack)
|
||||
apply(program,stack,Stack)
|
||||
}
|
||||
|
||||
|
||||
|
@ -319,32 +365,79 @@ object ScriptProgram {
|
|||
* @return
|
||||
*/
|
||||
def factory(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram = {
|
||||
factory(txSignatureComponent.transaction,txSignatureComponent.scriptPubKey,txSignatureComponent.inputIndex,
|
||||
apply(txSignatureComponent.transaction,txSignatureComponent.scriptPubKey,txSignatureComponent.inputIndex,
|
||||
stack,script,txSignatureComponent.flags)
|
||||
}
|
||||
|
||||
def apply(oldProgram : ScriptProgram, valid : Boolean) : ScriptProgram = factory(oldProgram, valid)
|
||||
def apply(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = factory(oldProgram, error)
|
||||
|
||||
def apply(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = factory(oldProgram, flags)
|
||||
|
||||
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram =
|
||||
factory(oldProgram, tokens, indicator)
|
||||
|
||||
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram =
|
||||
factory(oldProgram, stackTokens, scriptTokens)
|
||||
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram =
|
||||
factory(oldProgram, stackTokens, scriptTokens, flags)
|
||||
def apply(oldProgram : ScriptProgram, lastCodeSeparator : Int) : ScriptProgram = factory(oldProgram, lastCodeSeparator)
|
||||
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ScriptProgram =
|
||||
|
||||
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = factory(oldProgram, lastCodeSeparator)
|
||||
|
||||
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram =
|
||||
factory(oldProgram, tokens, indicator, lastCodeSeparator)
|
||||
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], valid : Boolean) : ScriptProgram =
|
||||
factory(oldProgram, stack, script, valid)
|
||||
|
||||
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
|
||||
updateIndicator: UpdateIndicator) : ScriptProgram = factory(oldProgram, stack, script, altStack, updateIndicator)
|
||||
updateIndicator: UpdateIndicator) : ScriptProgram = factory(oldProgram, stack, script, altStack)
|
||||
|
||||
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int,
|
||||
flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, flags)
|
||||
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, flags)
|
||||
|
||||
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, script : Seq[ScriptToken],
|
||||
flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, script, flags)
|
||||
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, script, flags)
|
||||
|
||||
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : Int, stack : Seq[ScriptToken],
|
||||
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, stack, script, flags)
|
||||
|
||||
def apply(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram =
|
||||
factory(txSignatureComponent, stack, script)
|
||||
|
||||
|
||||
/**
|
||||
* Changes a program that is being executed inside o
|
||||
* @param executionInProgressScriptProgram
|
||||
* @return
|
||||
*/
|
||||
def toExecutedProgram(executionInProgressScriptProgram: ExecutionInProgressScriptProgram) : ExecutedScriptProgram = {
|
||||
ExecutedScriptProgramImpl(executionInProgressScriptProgram.txSignatureComponent, executionInProgressScriptProgram.stack,
|
||||
executionInProgressScriptProgram.script,executionInProgressScriptProgram.originalScript,executionInProgressScriptProgram.altStack,
|
||||
executionInProgressScriptProgram.flags,None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a script program that is pre execution and changes it to an execution in progress script program
|
||||
* @param preExecutionScriptProgram
|
||||
* @return
|
||||
*/
|
||||
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram) : ExecutionInProgressScriptProgram = {
|
||||
toExecutionInProgress(preExecutionScriptProgram,None)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a pre execution script program to a execution in progress script program with the given stack state
|
||||
* @param preExecutionScriptProgram
|
||||
* @param stack
|
||||
* @return
|
||||
*/
|
||||
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[List[ScriptToken]]) : ExecutionInProgressScriptProgram = {
|
||||
stack match {
|
||||
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,stackTokens,preExecutionScriptProgram.script,
|
||||
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, 0)
|
||||
case None =>
|
||||
ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,preExecutionScriptProgram.stack,preExecutionScriptProgram.script,
|
||||
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.scalacoin.script.arithmetic
|
||||
|
||||
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.scalacoin.script.error.{ScriptErrorUnknownError, ScriptErrorInvalidStackOperation}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.BitcoinSUtil
|
||||
|
@ -142,7 +143,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
"Script top must be OP_NUMEQUALVERIFY")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val numEqualProgram = ScriptProgram(program, program.stack, OP_NUMEQUAL :: program.script.tail)
|
||||
val numEqualResult = opNumEqual(numEqualProgram)
|
||||
|
@ -225,7 +226,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MIN requires at least two stack elements")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
|
@ -252,7 +253,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
"Script top must be OP_MAX")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
|
@ -281,7 +282,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
"Script top must be OP_WITHIN")
|
||||
if (program.stack.size < 3) {
|
||||
logger.error("OP_WITHIN requires at least 3 elements on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val c = program.stack.head
|
||||
val b = program.stack.tail.head
|
||||
|
@ -319,7 +320,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
program.stack.headOption match {
|
||||
case None =>
|
||||
logger.error("We need one stack element for performing a unary arithmetic operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
case Some(s : ScriptNumber) =>
|
||||
if (checkBitcoinIntByteSize(s)) {
|
||||
val newScriptNumber = op(s)
|
||||
|
@ -327,15 +328,19 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
}
|
||||
else {
|
||||
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
|
||||
ScriptProgram(program,false)
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
case Some(s : ScriptConstant) =>
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex))
|
||||
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
|
||||
performUnaryArithmeticOperation(newProgram, op)
|
||||
case Some(s : ScriptToken) =>
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error("Stack top must be a script number to perform an arithmetic operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,7 +353,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
private def performBinaryArithmeticOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = {
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("We must have two elements to perform a binary arithmetic operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
(program.stack.head, program.stack.tail.head) match {
|
||||
case (x : ScriptNumber, y : ScriptNumber) =>
|
||||
|
@ -357,8 +362,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
|
||||
}
|
||||
else {
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
case (x : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
|
@ -377,8 +384,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,7 +404,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("We need two stack elements for a binary boolean operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
(program.stack.head, program.stack.tail.head) match {
|
||||
case (x : ScriptNumber, y : ScriptNumber) =>
|
||||
|
@ -404,8 +413,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
|
||||
}
|
||||
else {
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error("Cannot perform boolean operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
case (x : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
|
@ -424,8 +435,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.scalacoin.script.bitwise
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.control.{OP_VERIFY, ControlOperationsInterpreter}
|
||||
|
@ -20,7 +21,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
|
|||
require(program.script.headOption.isDefined && program.script.head == OP_EQUAL, "Script operation must be OP_EQUAL")
|
||||
|
||||
if (program.stack.size < 2) {
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val h = program.stack.head
|
||||
val h1 = program.stack.tail.head
|
||||
|
@ -65,7 +66,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
|
|||
opVerify(newProgram)
|
||||
case false =>
|
||||
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.scalacoin.script.constant
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -85,7 +86,7 @@ trait ConstantInterpreter extends BitcoinSLogger {
|
|||
//check to see if we have the exact amount of bytes needed to be pushed onto the stack
|
||||
//if we do not, mark the program as invalid
|
||||
if (bytesNeeded == 0) ScriptProgram(program, ScriptNumberFactory.zero :: program.stack, newScript)
|
||||
else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) ScriptProgram(program,false)
|
||||
else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
else ScriptProgram(program, constant :: program.stack, newScript)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.scalacoin.script.control
|
||||
|
||||
import org.scalacoin.script.error.{ScriptErrorVerify, ScriptErrorOpReturn, ScriptErrorInvalidStackOperation, ScriptErrorUnbalancedConditional}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util._
|
||||
|
@ -25,10 +26,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
logger.debug("Parsed binary tree: " + binaryTree)
|
||||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
logger.error("We do not have a matching OP_ENDIF for every OP_IF we have")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
} else if (program.stack.isEmpty) {
|
||||
logger.error("We do not have any stack elements for our OP_IF")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
else if (program.stackTopIsTrue) {
|
||||
logger.debug("OP_IF stack top was true")
|
||||
|
@ -60,10 +61,10 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
|
||||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
logger.error("We do not have a matching OP_ENDIF for every OP_NOTIF we have")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
} else if (program.stack.isEmpty) {
|
||||
logger.error("We do not have any stack elements for our OP_NOTIF")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else if (program.stackTopIsTrue) {
|
||||
//remove the OP_NOTIF
|
||||
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
|
||||
|
@ -114,7 +115,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
//means we do not have a matching OP_IF for our OP_ENDIF
|
||||
logger.error("We do not have a matching OP_IF/OP_NOTIF for every OP_ENDIF we have")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorUnbalancedConditional)
|
||||
} else ScriptProgram(program, program.stack,program.script.tail)
|
||||
|
||||
}
|
||||
|
@ -130,7 +131,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
def opReturn(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_RETURN)
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorOpReturn)
|
||||
}
|
||||
|
||||
|
||||
|
@ -141,14 +142,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
def opVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
|
||||
program.script.size > 0 match {
|
||||
program.stack.size > 0 match {
|
||||
case true =>
|
||||
logger.debug("Stack for OP_VERIFY: " + program.stack)
|
||||
if (program.stackTopIsFalse) ScriptProgram(program,false)
|
||||
if (program.stackTopIsFalse) ScriptProgram(program,ScriptErrorVerify)
|
||||
else ScriptProgram(program, program.stack.tail,program.script.tail)
|
||||
case false =>
|
||||
logger.error("OP_VERIFY requires an element to be on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import org.scalacoin.crypto._
|
|||
import org.scalacoin.protocol.script._
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.scalacoin.script.error.{ScriptErrorSigCount, ScriptErrorSigNullDummy, ScriptErrorSigDer, ScriptErrorInvalidStackOperation}
|
||||
import org.scalacoin.script.flag.{ScriptVerifyNullDummy, ScriptVerifyDerSig}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.{ExecutionInProgressScriptProgram, ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -80,7 +81,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_CHECKSIG requires at lest two stack elements")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val pubKey = ECFactory.publicKey(program.stack.head.bytes)
|
||||
val signature = ECFactory.digitalSignature(program.stack.tail.head.bytes)
|
||||
|
@ -91,7 +92,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
//script verification fails since the sig is not strictly der encoded
|
||||
logger.warn("Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
|
||||
"Sig: " + signature.hex)
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorSigDer)
|
||||
} else {
|
||||
val restOfStack = program.stack.tail.tail
|
||||
|
||||
|
@ -103,10 +104,11 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
case SignatureValidationSuccess => ScriptProgram(program,
|
||||
ScriptTrue :: restOfStack,program.script.tail)
|
||||
case SignatureValidationFailureNotStrictDerEncoding =>
|
||||
ScriptProgram(program, ScriptFalse :: restOfStack,
|
||||
program.script.tail,SignatureValidationFailureNotStrictDerEncoding.isValid)
|
||||
ScriptProgram(program, ScriptErrorSigDer)
|
||||
case SignatureValidationFailureIncorrectSignatures =>
|
||||
ScriptProgram(program, ScriptFalse :: restOfStack,program.script.tail)
|
||||
case SignatureValidationFailureSignatureCount =>
|
||||
ScriptProgram(program, ScriptFalse :: restOfStack,program.script.tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +133,10 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
}
|
||||
val indexOfOpCodeSeparator = fullScript.indexOf(OP_CODESEPARATOR)
|
||||
require(indexOfOpCodeSeparator != -1,"The script we searched MUST contain an OP_CODESEPARTOR. Script: " + fullScript)
|
||||
ScriptProgram(program,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
|
||||
val e = program match {
|
||||
case e : ExecutionInProgressScriptProgram => e
|
||||
}
|
||||
ScriptProgram(e,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,10 +158,10 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
|
||||
if (program.flags.contains(ScriptVerifyNullDummy) && program.txSignatureComponent.scriptSignature.asm.head != OP_0) {
|
||||
logger.warn("Script flag null dummy was set however the first element in the script signature was not an OP_0")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorSigNullDummy)
|
||||
} else if (program.stack.size < 3) {
|
||||
logger.error("OP_CHECKMULTISIG requires at least 3 stack elements")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
//these next lines remove the appropriate stack/script values after the signatures have been checked
|
||||
val nPossibleSignatures : Int = program.stack.head match {
|
||||
|
@ -201,11 +206,14 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
//set the valid flag to false on the script
|
||||
//see BIP66 for more information on this
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
|
||||
ScriptProgram(program, restOfStack, program.script.tail,false)
|
||||
ScriptProgram(program, ScriptErrorSigDer)
|
||||
case SignatureValidationFailureIncorrectSignatures =>
|
||||
//this means that signature verification failed, however all signatures were encoded correctly
|
||||
//just push a ScriptFalse onto the stack
|
||||
ScriptProgram(program, ScriptFalse :: restOfStack, program.script.tail)
|
||||
case SignatureValidationFailureSignatureCount =>
|
||||
//means that we did not have enough signatures for OP_CHECKMULTISIG
|
||||
ScriptProgram(program, ScriptErrorSigCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,13 +226,17 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
*/
|
||||
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY")
|
||||
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
|
||||
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
|
||||
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
|
||||
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
|
||||
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack)
|
||||
val programFromOpVerify = opVerify(programFromOpCheckMultiSig)
|
||||
programFromOpVerify
|
||||
if (program.stack.size < 3) {
|
||||
logger.error("Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
|
||||
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
|
||||
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
|
||||
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack)
|
||||
val programFromOpVerify = opVerify(programFromOpCheckMultiSig)
|
||||
programFromOpVerify
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -243,7 +255,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
ScriptProgram(program, hash :: program.stack.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("We must have the stack top defined to execute a hash function")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ package org.scalacoin.script.interpreter
|
|||
|
||||
import org.scalacoin.protocol.script._
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.error._
|
||||
import org.scalacoin.script.flag._
|
||||
import org.scalacoin.script.locktime.{OP_CHECKLOCKTIMEVERIFY, LockTimeInterpreter}
|
||||
import org.scalacoin.script.splice._
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
|
||||
import org.scalacoin.script.arithmetic._
|
||||
import org.scalacoin.script.bitwise._
|
||||
import org.scalacoin.script.constant._
|
||||
|
@ -55,186 +56,193 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
* @return program the final state of the program after being evaluated by the interpreter
|
||||
*/
|
||||
@tailrec
|
||||
def loop(program : ScriptProgram) : (Boolean,ScriptProgram) = {
|
||||
def loop(program : ScriptProgram) : (Boolean,ExecutedScriptProgram) = {
|
||||
logger.debug("Stack: " + program.stack)
|
||||
logger.debug("Script: " + program.script)
|
||||
if (program.script.headOption.isDefined &&
|
||||
BitcoinScriptUtil.countsTowardsScriptOpLimit(program.script.head)) opCount = opCount + 1
|
||||
|
||||
if (opCount > maxScriptOps) {
|
||||
if (opCount > maxScriptOps && !program.isInstanceOf[ExecutedScriptProgram]) {
|
||||
logger.error("We have reached the maximum amount of script operations allowed")
|
||||
logger.error("Here are the remaining operations in the script: " + program.script)
|
||||
(false,ScriptProgram(program,false))
|
||||
} else if (program.script.flatMap(_.bytes).size > 10000) {
|
||||
loop(ScriptProgram(program,ScriptErrorOpCount))
|
||||
} else if (program.script.flatMap(_.bytes).size > 10000 && !program.isInstanceOf[ExecutedScriptProgram]) {
|
||||
logger.error("We cannot run a script that is larger than 10,000 bytes")
|
||||
(false,ScriptProgram(program,false))
|
||||
program match {
|
||||
case p : PreExecutionScriptProgram =>
|
||||
loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize))
|
||||
case _ : ExecutionInProgressScriptProgram | _ : ExecutedScriptProgram =>
|
||||
loop(ScriptProgram(program, ScriptErrorScriptSize))
|
||||
}
|
||||
} else {
|
||||
program.script match {
|
||||
//if at any time we see that the program is not valid
|
||||
//cease script execution
|
||||
case _ if !program.isValid =>
|
||||
logger.error("Script program was marked as invalid: " + program)
|
||||
(false, ScriptProgram(program, false))
|
||||
case _ if !program.script.intersect(Seq(OP_VERIF, OP_VERNOTIF)).isEmpty =>
|
||||
logger.error("Script is invalid even when a OP_VERIF or OP_VERNOTIF occurs in an unexecuted OP_IF branch")
|
||||
(false, ScriptProgram(program, false))
|
||||
//disabled splice operation
|
||||
case _ if !program.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled splice operation")
|
||||
(false, ScriptProgram(program, false))
|
||||
//disabled bitwise operations
|
||||
case _ if !program.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled bitwise operation")
|
||||
(false, ScriptProgram(program, false))
|
||||
//disabled arithmetic operations
|
||||
case _ if !program.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled arithmetic operation")
|
||||
(false, ScriptProgram(program, false))
|
||||
//program cannot contain a push operation > 520 bytes
|
||||
case _ if (program.script.exists(token => token.bytes.size > 520)) =>
|
||||
logger.error("We have a script constant that is larger than 520 bytes, this is illegal: " + program.script)
|
||||
loop(ScriptProgram(program, false))
|
||||
//program stack size cannot be greater than 1000 elements
|
||||
case _ if ((program.stack.size + program.altStack.size) > 1000) =>
|
||||
logger.error("We cannot have a stack + alt stack size larger than 1000 elements")
|
||||
loop(ScriptProgram(program, false))
|
||||
|
||||
//stack operations
|
||||
case OP_DUP :: t => loop(opDup(program))
|
||||
case OP_DEPTH :: t => loop(opDepth(program))
|
||||
case OP_TOALTSTACK :: t => loop(opToAltStack(program))
|
||||
case OP_FROMALTSTACK :: t => loop(opFromAltStack(program))
|
||||
case OP_DROP :: t => loop(opDrop(program))
|
||||
case OP_IFDUP :: t => loop(opIfDup(program))
|
||||
case OP_NIP :: t => loop(opNip(program))
|
||||
case OP_OVER :: t => loop(opOver(program))
|
||||
case OP_PICK :: t => loop(opPick(program))
|
||||
case OP_ROLL :: t => loop(opRoll(program))
|
||||
case OP_ROT :: t => loop(opRot(program))
|
||||
case OP_2ROT :: t => loop(op2Rot(program))
|
||||
case OP_2DROP :: t => loop(op2Drop(program))
|
||||
case OP_SWAP :: t => loop(opSwap(program))
|
||||
case OP_TUCK :: t => loop(opTuck(program))
|
||||
case OP_2DUP :: t => loop(op2Dup(program))
|
||||
case OP_3DUP :: t => loop(op3Dup(program))
|
||||
case OP_2OVER :: t => loop(op2Over(program))
|
||||
case OP_2SWAP :: t => loop(op2Swap(program))
|
||||
|
||||
//arithmetic operations
|
||||
case OP_ADD :: t => loop(opAdd(program))
|
||||
case OP_1ADD :: t => loop(op1Add(program))
|
||||
case OP_1SUB :: t => loop(op1Sub(program))
|
||||
case OP_SUB :: t => loop(opSub(program))
|
||||
case OP_ABS :: t => loop(opAbs(program))
|
||||
case OP_NEGATE :: t => loop(opNegate(program))
|
||||
case OP_NOT :: t => loop(opNot(program))
|
||||
case OP_0NOTEQUAL :: t => loop(op0NotEqual(program))
|
||||
case OP_BOOLAND :: t => loop(opBoolAnd(program))
|
||||
case OP_BOOLOR :: t => loop(opBoolOr(program))
|
||||
case OP_NUMEQUAL :: t => loop(opNumEqual(program))
|
||||
case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(program))
|
||||
case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(program))
|
||||
case OP_LESSTHAN :: t => loop(opLessThan(program))
|
||||
case OP_GREATERTHAN :: t => loop(opGreaterThan(program))
|
||||
case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(program))
|
||||
case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(program))
|
||||
case OP_MIN :: t => loop(opMin(program))
|
||||
case OP_MAX :: t => loop(opMax(program))
|
||||
case OP_WITHIN :: t => loop(opWithin(program))
|
||||
|
||||
//bitwise operations
|
||||
case OP_EQUAL :: t =>
|
||||
val newProgram = opEqual(program)
|
||||
loop(newProgram)
|
||||
|
||||
case OP_EQUALVERIFY :: t => loop(opEqualVerify(program))
|
||||
|
||||
case (scriptNumberOp: ScriptNumberOperation) :: t =>
|
||||
if (scriptNumberOp == OP_0) loop(ScriptProgram(program, ScriptNumberFactory.zero :: program.stack, t))
|
||||
else loop(ScriptProgram(program, ScriptNumberFactory.fromNumber(scriptNumberOp.num) :: program.stack, t))
|
||||
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => loop(pushScriptNumberBytesToStack(program))
|
||||
case (scriptNumber: ScriptNumber) :: t =>
|
||||
loop(ScriptProgram(program, scriptNumber :: program.stack, t))
|
||||
case OP_PUSHDATA1 :: t => loop(opPushData1(program))
|
||||
case OP_PUSHDATA2 :: t => loop(opPushData2(program))
|
||||
case OP_PUSHDATA4 :: t => loop(opPushData4(program))
|
||||
|
||||
case (x : ScriptConstant) :: t => loop(ScriptProgram(program, x :: program.stack, t))
|
||||
|
||||
//control operations
|
||||
case OP_IF :: t => loop(opIf(program))
|
||||
case OP_NOTIF :: t => loop(opNotIf(program))
|
||||
case OP_ELSE :: t => loop(opElse(program))
|
||||
case OP_ENDIF :: t => loop(opEndIf(program))
|
||||
case OP_RETURN :: t =>
|
||||
val newProgram = opReturn(program)
|
||||
(newProgram.isValid, newProgram)
|
||||
case OP_VERIFY :: t => loop(opVerify(program))
|
||||
|
||||
//crypto operations
|
||||
case OP_HASH160 :: t => loop(opHash160(program))
|
||||
case OP_CHECKSIG :: t => loop(opCheckSig(program))
|
||||
case OP_SHA1 :: t => loop(opSha1(program))
|
||||
case OP_RIPEMD160 :: t => loop(opRipeMd160(program))
|
||||
case OP_SHA256 :: t => loop(opSha256(program))
|
||||
case OP_HASH256 :: t => loop(opHash256(program))
|
||||
case OP_CODESEPARATOR :: t => loop(opCodeSeparator(program))
|
||||
case OP_CHECKMULTISIG :: t => loop(opCheckMultiSig(program))
|
||||
case OP_CHECKMULTISIGVERIFY :: t => loop(opCheckMultiSigVerify(program))
|
||||
//reserved operations
|
||||
case OP_NOP :: t =>
|
||||
//script discourage upgradeable flag does not apply to a OP_NOP
|
||||
loop(ScriptProgram(program, program.stack, t))
|
||||
|
||||
//if we see an OP_NOP and the DISCOURAGE_UPGRADABLE_OP_NOPS flag is set we must fail our program
|
||||
case (nop: NOP) :: t if ScriptFlagUtil.discourageUpgradableNOPs(program.flags) =>
|
||||
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
(false, ScriptProgram(program, false))
|
||||
case (nop: NOP) :: t => loop(ScriptProgram(program, program.stack, t))
|
||||
case OP_RESERVED :: t =>
|
||||
logger.error("OP_RESERVED automatically marks transaction invalid")
|
||||
(false, program)
|
||||
case OP_VER :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_VER")
|
||||
(false, program)
|
||||
case OP_RESERVED1 :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED1")
|
||||
(false, program)
|
||||
case OP_RESERVED2 :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED2")
|
||||
(false, program)
|
||||
|
||||
case (reservedOperation : ReservedOperation) :: t =>
|
||||
logger.error("Undefined operation found which automatically fails the script: " + reservedOperation)
|
||||
loop(ScriptProgram(program,false))
|
||||
//splice operations
|
||||
case OP_SIZE :: t => loop(opSize(program))
|
||||
|
||||
//locktime operations
|
||||
case OP_CHECKLOCKTIMEVERIFY :: t =>
|
||||
//check if CLTV is enforced yet
|
||||
if (ScriptFlagUtil.checkLockTimeVerifyEnabled(program.flags)) loop(opCheckLockTimeVerify(program))
|
||||
//if not, check to see if we should discourage NOPs
|
||||
else if (ScriptFlagUtil.discourageUpgradableNOPs(program.flags)) {
|
||||
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
(false, ScriptProgram(program, false))
|
||||
}
|
||||
//in this case, just reat OP_CLTV just like a NOP and remove it from the stack
|
||||
else loop(ScriptProgram(program, program.script.tail, ScriptProgram.Script))
|
||||
//no more script operations to run, return whether the program is valid and the final state of the program
|
||||
case Nil =>
|
||||
program match {
|
||||
case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)))
|
||||
case p : ExecutedScriptProgram =>
|
||||
//reset opCount variable to zero since we may need to count the ops
|
||||
//in the scriptPubKey - we don't want the op count of the scriptSig
|
||||
//to count towards the scriptPubKey op count
|
||||
opCount = 0
|
||||
(program.isValid, program)
|
||||
(!p.error.isDefined, p)
|
||||
case p : ExecutionInProgressScriptProgram =>
|
||||
//increment the op count
|
||||
if (p.script.headOption.isDefined &&
|
||||
BitcoinScriptUtil.countsTowardsScriptOpLimit(p.script.head)) opCount = opCount + 1
|
||||
p.script match {
|
||||
//if at any time we see that the program is not valid
|
||||
//cease script execution
|
||||
case _ if !p.script.intersect(Seq(OP_VERIF, OP_VERNOTIF)).isEmpty =>
|
||||
logger.error("Script is invalid even when a OP_VERIF or OP_VERNOTIF occurs in an unexecuted OP_IF branch")
|
||||
loop(ScriptProgram(p, ScriptErrorDisabledOpCode))
|
||||
//disabled splice operation
|
||||
case _ if !p.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled splice operation")
|
||||
loop(ScriptProgram(p, ScriptErrorDisabledOpCode))
|
||||
//disabled bitwise operations
|
||||
case _ if !p.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled bitwise operation")
|
||||
loop(ScriptProgram(p, ScriptErrorDisabledOpCode))
|
||||
//disabled arithmetic operations
|
||||
case _ if !p.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).isEmpty =>
|
||||
logger.error("Script is invalid because it contains a disabled arithmetic operation")
|
||||
loop(ScriptProgram(p, ScriptErrorDisabledOpCode))
|
||||
//program cannot contain a push operation > 520 bytes
|
||||
case _ if (p.script.exists(token => token.bytes.size > 520)) =>
|
||||
logger.error("We have a script constant that is larger than 520 bytes, this is illegal: " + p.script)
|
||||
loop(ScriptProgram(p, ScriptErrorPushSize))
|
||||
//program stack size cannot be greater than 1000 elements
|
||||
case _ if ((p.stack.size + p.altStack.size) > 1000) =>
|
||||
logger.error("We cannot have a stack + alt stack size larger than 1000 elements")
|
||||
loop(ScriptProgram(p, ScriptErrorStackSize))
|
||||
|
||||
case h :: t => throw new RuntimeException(h + " was unmatched")
|
||||
//stack operations
|
||||
case OP_DUP :: t => loop(opDup(p))
|
||||
case OP_DEPTH :: t => loop(opDepth(p))
|
||||
case OP_TOALTSTACK :: t => loop(opToAltStack(p))
|
||||
case OP_FROMALTSTACK :: t => loop(opFromAltStack(p))
|
||||
case OP_DROP :: t => loop(opDrop(p))
|
||||
case OP_IFDUP :: t => loop(opIfDup(p))
|
||||
case OP_NIP :: t => loop(opNip(p))
|
||||
case OP_OVER :: t => loop(opOver(p))
|
||||
case OP_PICK :: t => loop(opPick(p))
|
||||
case OP_ROLL :: t => loop(opRoll(p))
|
||||
case OP_ROT :: t => loop(opRot(p))
|
||||
case OP_2ROT :: t => loop(op2Rot(p))
|
||||
case OP_2DROP :: t => loop(op2Drop(p))
|
||||
case OP_SWAP :: t => loop(opSwap(p))
|
||||
case OP_TUCK :: t => loop(opTuck(p))
|
||||
case OP_2DUP :: t => loop(op2Dup(p))
|
||||
case OP_3DUP :: t => loop(op3Dup(p))
|
||||
case OP_2OVER :: t => loop(op2Over(p))
|
||||
case OP_2SWAP :: t => loop(op2Swap(p))
|
||||
|
||||
//arithmetic operations
|
||||
case OP_ADD :: t => loop(opAdd(p))
|
||||
case OP_1ADD :: t => loop(op1Add(p))
|
||||
case OP_1SUB :: t => loop(op1Sub(p))
|
||||
case OP_SUB :: t => loop(opSub(p))
|
||||
case OP_ABS :: t => loop(opAbs(p))
|
||||
case OP_NEGATE :: t => loop(opNegate(p))
|
||||
case OP_NOT :: t => loop(opNot(p))
|
||||
case OP_0NOTEQUAL :: t => loop(op0NotEqual(p))
|
||||
case OP_BOOLAND :: t => loop(opBoolAnd(p))
|
||||
case OP_BOOLOR :: t => loop(opBoolOr(p))
|
||||
case OP_NUMEQUAL :: t => loop(opNumEqual(p))
|
||||
case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(p))
|
||||
case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(p))
|
||||
case OP_LESSTHAN :: t => loop(opLessThan(p))
|
||||
case OP_GREATERTHAN :: t => loop(opGreaterThan(p))
|
||||
case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(p))
|
||||
case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(p))
|
||||
case OP_MIN :: t => loop(opMin(p))
|
||||
case OP_MAX :: t => loop(opMax(p))
|
||||
case OP_WITHIN :: t => loop(opWithin(p))
|
||||
|
||||
//bitwise operations
|
||||
case OP_EQUAL :: t =>
|
||||
val newProgram = opEqual(p)
|
||||
loop(newProgram)
|
||||
|
||||
case OP_EQUALVERIFY :: t => loop(opEqualVerify(p))
|
||||
|
||||
case (scriptNumberOp : ScriptNumberOperation) :: t =>
|
||||
if (scriptNumberOp == OP_0) loop(ScriptProgram(p, ScriptNumberFactory.zero :: p.stack, t))
|
||||
else loop(ScriptProgram(p, ScriptNumberFactory.fromNumber(scriptNumberOp.num) :: p.stack, t))
|
||||
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => loop(pushScriptNumberBytesToStack(p))
|
||||
case (scriptNumber: ScriptNumber) :: t =>
|
||||
loop(ScriptProgram(p, scriptNumber :: p.stack, t))
|
||||
case OP_PUSHDATA1 :: t => loop(opPushData1(p))
|
||||
case OP_PUSHDATA2 :: t => loop(opPushData2(p))
|
||||
case OP_PUSHDATA4 :: t => loop(opPushData4(p))
|
||||
|
||||
case (x : ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t))
|
||||
|
||||
//control operations
|
||||
case OP_IF :: t => loop(opIf(p))
|
||||
case OP_NOTIF :: t => loop(opNotIf(p))
|
||||
case OP_ELSE :: t => loop(opElse(p))
|
||||
case OP_ENDIF :: t => loop(opEndIf(p))
|
||||
case OP_RETURN :: t => loop(opReturn(p))
|
||||
|
||||
case OP_VERIFY :: t => loop(opVerify(p))
|
||||
|
||||
//crypto operations
|
||||
case OP_HASH160 :: t => loop(opHash160(p))
|
||||
case OP_CHECKSIG :: t => loop(opCheckSig(p))
|
||||
case OP_SHA1 :: t => loop(opSha1(p))
|
||||
case OP_RIPEMD160 :: t => loop(opRipeMd160(p))
|
||||
case OP_SHA256 :: t => loop(opSha256(p))
|
||||
case OP_HASH256 :: t => loop(opHash256(p))
|
||||
case OP_CODESEPARATOR :: t => loop(opCodeSeparator(p))
|
||||
case OP_CHECKMULTISIG :: t => loop(opCheckMultiSig(p))
|
||||
case OP_CHECKMULTISIGVERIFY :: t => loop(opCheckMultiSigVerify(p))
|
||||
//reserved operations
|
||||
case OP_NOP :: t =>
|
||||
//script discourage upgradeable flag does not apply to a OP_NOP
|
||||
loop(ScriptProgram(p, p.stack, t))
|
||||
|
||||
//if we see an OP_NOP and the DISCOURAGE_UPGRADABLE_OP_NOPS flag is set we must fail our program
|
||||
case (nop: NOP) :: t if ScriptFlagUtil.discourageUpgradableNOPs(p.flags) =>
|
||||
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs))
|
||||
case (nop: NOP) :: t => loop(ScriptProgram(p, p.stack, t))
|
||||
case OP_RESERVED :: t =>
|
||||
logger.error("OP_RESERVED automatically marks transaction invalid")
|
||||
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
|
||||
case OP_VER :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_VER")
|
||||
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
|
||||
case OP_RESERVED1 :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED1")
|
||||
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
|
||||
case OP_RESERVED2 :: t =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED2")
|
||||
loop(ScriptProgram(p,ScriptErrorDisabledOpCode))
|
||||
|
||||
case (reservedOperation : ReservedOperation) :: t =>
|
||||
logger.error("Undefined operation found which automatically fails the script: " + reservedOperation)
|
||||
loop(ScriptProgram(p,ScriptErrorBadOpCode))
|
||||
//splice operations
|
||||
case OP_SIZE :: t => loop(opSize(p))
|
||||
|
||||
//locktime operations
|
||||
case OP_CHECKLOCKTIMEVERIFY :: t =>
|
||||
//check if CLTV is enforced yet
|
||||
if (ScriptFlagUtil.checkLockTimeVerifyEnabled(p.flags)) loop(opCheckLockTimeVerify(p))
|
||||
//if not, check to see if we should discourage p
|
||||
else if (ScriptFlagUtil.discourageUpgradableNOPs(p.flags)) {
|
||||
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs))
|
||||
}
|
||||
//in this case, just reat OP_CLTV just like a NOP and remove it from the stack
|
||||
else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script))
|
||||
//no more script operations to run, return whether the program is valid and the final state of the program
|
||||
case Nil => loop(ScriptProgram.toExecutedProgram(p))
|
||||
case h :: t => throw new RuntimeException(h + " was unmatched")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val scriptSigProgram = ScriptProgram(program,Seq(),program.txSignatureComponent.scriptSignature.asm)
|
||||
|
||||
val (result,executedProgram) = program.txSignatureComponent.scriptSignature match {
|
||||
//if the P2SH script flag is not set, we evaluate a p2sh scriptSig just like any other scriptSig
|
||||
|
@ -251,7 +259,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
val stack = BitcoinScriptUtil.filterPushOps(scriptSig.scriptSignatureNoRedeemScript.asm.reverse)
|
||||
logger.debug("P2sh stack: " + stack)
|
||||
logger.debug("P2sh redeemScript: " + scriptSig.redeemScript.asm)
|
||||
val p2shRedeemScriptProgram = ScriptProgram(hashesMatchProgram,stack, scriptSig.redeemScript.asm)
|
||||
val p2shRedeemScriptProgram = ScriptProgram(hashesMatchProgram.txSignatureComponent,stack, scriptSig.redeemScript.asm)
|
||||
loop(p2shRedeemScriptProgram)
|
||||
case false =>
|
||||
logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript")
|
||||
|
@ -260,16 +268,21 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : MultiSignatureScriptSignature |
|
||||
_ : NonStandardScriptSignature | _ : P2SHScriptSignature | EmptyScriptSignature =>
|
||||
|
||||
val scriptSigProgram = ScriptProgram(program,Seq(),program.txSignatureComponent.scriptSignature.asm)
|
||||
val (scriptSigProgramIsValid,scriptSigExecutedProgram) = loop(scriptSigProgram)
|
||||
logger.info("Stack state after scriptSig execution: " + scriptSigExecutedProgram.stack)
|
||||
logger.info("scriptSigExecutedProgram: " + scriptSigExecutedProgram.error)
|
||||
logger.info("scriptSigProgramIsValid: " + scriptSigProgramIsValid)
|
||||
if (scriptSigProgramIsValid) {
|
||||
logger.debug("We do not check a redeemScript against a non p2sh scriptSig")
|
||||
//now run the scriptPubKey script through the interpreter with the scriptSig as the stack arguments
|
||||
val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent,
|
||||
scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
|
||||
require(scriptPubKeyProgram.script == scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
|
||||
val (scriptPubKeyProgramIsValid, scriptPubKeyExecutedProgram) = loop(scriptPubKeyProgram)
|
||||
|
||||
logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack)
|
||||
|
||||
//if the program is valid, return if the stack top is true
|
||||
//else the program is false since something illegal happened during script evaluation
|
||||
scriptPubKeyProgramIsValid match {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package org.scalacoin.script.locktime
|
||||
|
||||
import org.scalacoin.protocol.transaction.TransactionConstants
|
||||
import org.scalacoin.script.constant.{ScriptNumberFactory, ScriptNumberImpl, ScriptNumber}
|
||||
import org.scalacoin.script.constant.{ScriptToken, ScriptNumberFactory, ScriptNumberImpl, ScriptNumber}
|
||||
import org.scalacoin.script.error.{ScriptError, ScriptErrorNegativeLockTime, ScriptErrorUnsatisfiedLocktime, ScriptErrorInvalidStackOperation}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.util.BitcoinSLogger
|
||||
|
||||
|
@ -28,25 +29,26 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
"Script top must be OP_CHECKLOCKTIMEVERIFY")
|
||||
if (program.stack.size == 0) {
|
||||
logger.warn("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because we have no stack items")
|
||||
ScriptProgram(program, program.stack, program.script.tail, false)
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
} else if (program.txSignatureComponent.transaction.inputs(program.txSignatureComponent.inputIndex).sequence == TransactionConstants.sequence) {
|
||||
logger.warn("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because the sequence number is 0xffffffff")
|
||||
ScriptProgram(program, program.stack, program.script.tail, false)
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
}
|
||||
else {
|
||||
val isValid = program.stack.head match {
|
||||
val isError : Option[ScriptError] = program.stack.head match {
|
||||
case s : ScriptNumber if (s < ScriptNumberFactory.zero) =>
|
||||
logger.warn("OP_CHECKLOCKTIMEVERIFY marks tx as invalid if the stack top is negative")
|
||||
false
|
||||
Some(ScriptErrorNegativeLockTime)
|
||||
case s : ScriptNumber if (s >= ScriptNumberFactory.fromNumber(500000000) && program.txSignatureComponent.transaction.lockTime < 500000000) =>
|
||||
logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top >= 500000000 & tx locktime < 500000000")
|
||||
false
|
||||
Some(ScriptErrorUnsatisfiedLocktime)
|
||||
case s : ScriptNumber if (s < ScriptNumberFactory.fromNumber(500000000) && program.txSignatureComponent.transaction.lockTime >= 500000000) =>
|
||||
logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
|
||||
false
|
||||
case _ => true
|
||||
Some(ScriptErrorUnsatisfiedLocktime)
|
||||
case _ : ScriptToken => None
|
||||
}
|
||||
ScriptProgram(program,program.stack, program.script.tail, isValid)
|
||||
if (isError.isDefined) ScriptProgram(program,isError.get)
|
||||
else ScriptProgram(program,program.stack, program.script.tail)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.scalacoin.script.splice
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptOperationFactory, ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import Math._
|
||||
|
@ -32,7 +33,7 @@ trait SpliceInterpreter extends BitcoinSLogger {
|
|||
}
|
||||
case false =>
|
||||
logger.error("Must have at least 1 element on the stack for OP_SIZE")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.scalacoin.script.stack
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
|
||||
|
@ -23,7 +24,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
case h :: t => ScriptProgram(program, h :: program.stack, program.script.tail)
|
||||
case Nil =>
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +42,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
program.script.tail)
|
||||
case false =>
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
program.script.tail, program.stack.head :: program.altStack, ScriptProgram.AltStack)
|
||||
case false =>
|
||||
logger.error("OP_TOALTSTACK requires an element to be on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
program.script.tail, program.altStack.tail, ScriptProgram.AltStack)
|
||||
case false =>
|
||||
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +105,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
case true => ScriptProgram(program, program.stack.tail,program.script.tail)
|
||||
case false =>
|
||||
logger.error("Stack must have at least one item on it for OP_DROP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -121,10 +122,10 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
case h :: _ :: t => ScriptProgram(program, h :: t, program.script.tail)
|
||||
case h :: t =>
|
||||
logger.error("Stack must have at least two items on it for OP_NIP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
case Nil =>
|
||||
logger.error("Stack must have at least two items on it for OP_NIP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,10 +140,10 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
program.stack match {
|
||||
case _ :: h1 :: _ => ScriptProgram(program, h1 :: program.stack, program.script.tail)
|
||||
case h :: t => logger.error("Stack must have at least two items on it for OP_OVER")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
case Nil =>
|
||||
logger.error("Stack must have at least two items on it for OP_OVER")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +164,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program,newStackTop :: program.stack.tail, program.script.tail)
|
||||
case false =>
|
||||
logger.error("The index for OP_PICK would have caused an index out of bounds exception")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -185,7 +186,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program,newStack,program.script.tail)
|
||||
case false =>
|
||||
logger.error("The index for OP_ROLL would have caused an index out of bounds exception")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +205,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 3 items on it for OP_ROT")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -223,7 +224,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("OP_2ROT requires 6 elements on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -240,7 +241,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
|
||||
case false =>
|
||||
logger.error("OP_2DROP requires two elements to be on the stack")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -259,7 +260,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case false =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_SWAP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -280,7 +281,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_TUCK")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -300,7 +301,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_2DUP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -318,7 +319,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program,newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 3 items on it for OP_3DUP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -339,7 +340,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program, newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 4 items on it for OP_2OVER")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -358,7 +359,7 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
ScriptProgram(program,newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 4 items on it for OP_2SWAP")
|
||||
ScriptProgram(program,false)
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.scalacoin.script
|
||||
|
||||
import org.scalacoin.script.constant.{OP_1, OP_0}
|
||||
import org.scalacoin.script.flag.ScriptFlagFactory
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalatest.{MustMatchers, FlatSpec}
|
||||
|
||||
|
@ -24,14 +25,14 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
|
|||
|
||||
it must "update the OP_CODESEPARATOR index" in {
|
||||
val index = 999
|
||||
val program = ScriptProgram(TestUtil.testProgram,index)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,index)
|
||||
program.lastCodeSeparator must be (999)
|
||||
}
|
||||
|
||||
it must "update the OP_CODESEPARATOR index & stack simultaneously" in {
|
||||
val index = 999
|
||||
val stack = Seq(OP_0)
|
||||
val program = ScriptProgram(TestUtil.testProgram,stack,ScriptProgram.Stack,index)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,ScriptProgram.Stack,index)
|
||||
program.stack must be (stack)
|
||||
program.lastCodeSeparator must be (index)
|
||||
}
|
||||
|
@ -39,8 +40,17 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
|
|||
it must "update the OP_CODESEPARATOR index & altStack simultaneously" in {
|
||||
val index = 999
|
||||
val altStack = Seq(OP_0)
|
||||
val program = ScriptProgram(TestUtil.testProgram,altStack,ScriptProgram.AltStack,index)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,altStack,ScriptProgram.AltStack,index)
|
||||
program.altStack must be (altStack)
|
||||
program.lastCodeSeparator must be (index)
|
||||
}
|
||||
|
||||
|
||||
it must "update the script program to the given stack and script" in {
|
||||
val stack = List(OP_0)
|
||||
val script = List(OP_1)
|
||||
val program = ScriptProgram(TestUtil.transaction, TestUtil.scriptPubKey, 0, stack,script, ScriptFlagFactory.empty)
|
||||
program.stack must be (stack)
|
||||
program.script must be (script)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package org.scalacoin.script.arithmetic
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
|
@ -34,9 +35,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
|
||||
val stack = List()
|
||||
val script = List(OP_1ADD)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Add(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Add(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -54,9 +55,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
|
||||
val stack = List()
|
||||
val script = List(OP_1SUB)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Sub(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op1Sub(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "perform an OP_SUB corectly" in {
|
||||
|
@ -65,16 +66,16 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opSub(program)
|
||||
|
||||
newProgram.stack.head must be (ScriptNumberImpl(-1))
|
||||
newProgram.stack.head must be (ScriptNumberFactory.fromNumber(-1))
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "mark a script as invalid if we have an OP_SUB with nothing on the stack" in {
|
||||
val stack = List()
|
||||
val script = List(OP_SUB)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opSub(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opSub(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "perform an OP_ABS on a negative number corectly" in {
|
||||
|
@ -99,9 +100,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
it must "mark a script as invalid if we have an OP_ABS with nothing on the stack" in {
|
||||
val stack = List()
|
||||
val script = List(OP_ABS)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opAbs(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opAbs(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "perform an OP_NEGATE on a zero correctly" in {
|
||||
|
@ -137,9 +138,9 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
|
||||
val stack = List()
|
||||
val script = List(OP_NEGATE)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNegate(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNegate(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "perform an OP_NOT correctly where 0 is the stack top" in {
|
||||
|
@ -317,7 +318,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram1.stack.head must be (OP_FALSE)
|
||||
newProgram1.script.isEmpty must be (true)
|
||||
|
||||
val stack2 = List(OP_1, ScriptNumberImpl(0))
|
||||
val stack2 = List(OP_1, ScriptNumberFactory.zero)
|
||||
val script2 = List(OP_LESSTHAN)
|
||||
val program2 = ScriptProgram(TestUtil.testProgram, stack2,script2)
|
||||
val newProgram2 = opLessThan(program2)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.scalacoin.script.bitwise
|
||||
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.{ExecutedScriptProgram, ScriptProgram}
|
||||
import org.scalacoin.script.arithmetic.OP_NUMEQUAL
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.TestUtil
|
||||
|
@ -42,21 +42,22 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte
|
|||
}
|
||||
}
|
||||
|
||||
it must "evaulate OP_EQUALVERIFY to true given two of the same pub keys" in {
|
||||
it must "evaulate OP_EQUALVERIFY must not evaluate a transaction to invalid with two of the same pubkeys" in {
|
||||
val stack = List(pubKeyHash, pubKeyHash)
|
||||
val script = List(OP_EQUALVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val result = opEqualVerify(program)
|
||||
result.isValid must be (true)
|
||||
//if verification fails it will transform the script to a ExecutedProgram with an error set
|
||||
result.isInstanceOf[ExecutedScriptProgram] must be (false)
|
||||
}
|
||||
|
||||
it must "evaluate OP_EQUALVERIFY to false given two different pub keys" in {
|
||||
val uniquePubKey = ScriptConstantImpl(pubKeyHash.hex +"00")
|
||||
val stack = List(pubKeyHash,uniquePubKey)
|
||||
val script = List(OP_EQUALVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val result = opEqualVerify(program)
|
||||
result.isValid must be (false)
|
||||
result.stackTopIsTrue must be (false)
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,7 +69,7 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte
|
|||
|
||||
val stack1 = List( ScriptConstantImpl("02"),ScriptNumberFactory.fromNumber(2))
|
||||
val script1 = List(OP_EQUAL)
|
||||
val program1 = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val program1 = ScriptProgram(TestUtil.testProgram, stack1,script1)
|
||||
opEqual(program1).stack.head must be (ScriptTrue)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ package org.scalacoin.script.constant
|
|||
|
||||
import org.scalacoin.script.ScriptProgram
|
||||
import org.scalacoin.script.bitwise.OP_EQUAL
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
|
@ -52,7 +53,7 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
|
|||
val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.fromNumber(0).get)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opPushData1(program)
|
||||
newProgram.isValid must be (true)
|
||||
newProgram.stackTopIsFalse must be (true)
|
||||
newProgram.stack must be (List(ScriptNumberFactory.zero))
|
||||
|
||||
val stack1 = List()
|
||||
|
@ -72,8 +73,8 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
|
|||
it must "mark a program as invalid if we have do not have enough bytes to be pushed onto the stack by the push operation" in {
|
||||
val stack = List()
|
||||
val script = List(OP_PUSHDATA1,BytesToPushOntoStackFactory.factory(1).get)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opPushData1(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opPushData1(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package org.scalacoin.script.control
|
||||
|
||||
import org.scalacoin.marshallers.script.ScriptParser
|
||||
import org.scalacoin.script.error.{ScriptErrorOpReturn, ScriptErrorInvalidStackOperation}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.arithmetic.OP_ADD
|
||||
import org.scalacoin.script.bitwise.OP_EQUAL
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.reserved.{OP_VER, OP_RESERVED}
|
||||
import org.scalacoin.util.{TestUtil, Empty, Node, Leaf}
|
||||
import org.scalacoin.util._
|
||||
import org.scalatest.{MustMatchers, FlatSpec}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +22,6 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
|||
val result = opVerify(program)
|
||||
result.stack.isEmpty must be (true)
|
||||
result.script.isEmpty must be (true)
|
||||
result.isValid must be (true)
|
||||
}
|
||||
|
||||
it must "have OP_VERIFY evaluate to true when there are multiple items on the stack that can be cast to an int" in {
|
||||
|
@ -31,24 +31,23 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
|||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val result = opVerify(program)
|
||||
result.isValid must be (true)
|
||||
}
|
||||
|
||||
it must "have OP_VERIFY evaluate to false with '0' on the stack" in {
|
||||
val stack = List(ScriptFalse)
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val result = opVerify(program)
|
||||
result.isValid must be (false)
|
||||
result.stackTopIsFalse must be (true)
|
||||
}
|
||||
|
||||
it must "mark the script as invalid for OP_VERIFY when there is nothing on the stack" in {
|
||||
|
||||
val stack = List()
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val result = opVerify(program)
|
||||
result.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val result = ScriptProgramTestUtil.toExecutedScriptProgram(opVerify(program))
|
||||
result.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -468,10 +467,10 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
|||
it must "mark a transaction as invalid if it is trying to spend an OP_RETURN output" in {
|
||||
val stack = Seq()
|
||||
val script = Seq(OP_RETURN)
|
||||
val program = ScriptProgram(TestUtil.testProgram,stack,script)
|
||||
val newProgram = opReturn(program)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opReturn(program))
|
||||
|
||||
newProgram.isValid must be (false)
|
||||
newProgram.error must be (Some(ScriptErrorOpReturn))
|
||||
}
|
||||
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,10 @@
|
|||
package org.scalacoin.script.locktime
|
||||
|
||||
import org.scalacoin.protocol.transaction.{TransactionInput, Transaction, UpdateTransactionInputs}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.constant.{ScriptNumberImpl, OP_0}
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalacoin.script.error.{ScriptErrorNegativeLockTime, ScriptErrorUnsatisfiedLocktime, ScriptErrorInvalidStackOperation}
|
||||
import org.scalacoin.script.{ExecutionInProgressScriptProgram, ExecutedScriptProgram, PreExecutionScriptProgram, ScriptProgram}
|
||||
import org.scalacoin.script.constant.{ScriptNumberFactory, ScriptNumberImpl, OP_0}
|
||||
import org.scalacoin.util.{ScriptProgramTestUtil, TestUtil}
|
||||
import org.scalatest.{MustMatchers, FlatSpec}
|
||||
|
||||
/**
|
||||
|
@ -14,60 +15,60 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
|
|||
"LockTimeInterpreter" must "mark the transaction invalid if the stack is empty" in {
|
||||
val stack = Seq()
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "mark the transaction invalid if the transaction's sequence number is set to the max" in {
|
||||
val stack = Seq(OP_0)
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(program))
|
||||
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
|
||||
}
|
||||
|
||||
it must "mark the transaction as invalid if the stack top is negative" in {
|
||||
val stack = Seq(ScriptNumberImpl(-1))
|
||||
val stack = Seq(ScriptNumberFactory.fromNumber(-1))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
|
||||
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
|
||||
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
|
||||
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
|
||||
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
|
||||
val program = ScriptProgram(baseProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
|
||||
newProgram.error must be (Some(ScriptErrorNegativeLockTime))
|
||||
}
|
||||
|
||||
it must "mark the transaction as invalid if the locktime on the tx is < 500000000 && stack top is >= 500000000" in {
|
||||
val stack = Seq(ScriptNumberImpl(500000000))
|
||||
val stack = Seq(ScriptNumberFactory.fromNumber(500000000))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
|
||||
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
|
||||
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,0)
|
||||
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
|
||||
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
|
||||
val program = ScriptProgram(baseProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
|
||||
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
|
||||
}
|
||||
|
||||
it must "mark the transaction as invalid if the locktime on the tx is >= 500000000 && stack top is < 500000000" in {
|
||||
val stack = Seq(ScriptNumberImpl(499999999))
|
||||
val stack = Seq(ScriptNumberFactory.fromNumber(499999999))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
|
||||
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
|
||||
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000)
|
||||
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
|
||||
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
|
||||
val program = ScriptProgram(baseProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgramTestUtil.toPreExecutionScriptProgram(ScriptProgram(baseProgram,stack,script))
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opCheckLockTimeVerify(ScriptProgram.toExecutionInProgress(program)))
|
||||
newProgram.error must be (Some(ScriptErrorUnsatisfiedLocktime))
|
||||
}
|
||||
|
||||
it must "mark the transaction as valid if the locktime on the tx is < 500000000 && stack top is < 500000000" in {
|
||||
val stack = Seq(ScriptNumberImpl(499999999))
|
||||
val stack = Seq(ScriptNumberFactory.fromNumber(499999999))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
|
||||
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
|
||||
|
@ -76,20 +77,24 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
|
|||
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
|
||||
val program = ScriptProgram(baseProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (true)
|
||||
//if an error is hit, the newProgram will be an instance of ExecutedScriptProgram
|
||||
//if an error is not hit it will still be a ExecutionInProgressScriptProgram
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
|
||||
}
|
||||
|
||||
it must "mark the transaction as valid if the locktime on the tx is >= 500000000 && stack top is >= 500000000" in {
|
||||
val stack = Seq(ScriptNumberImpl(500000000))
|
||||
val stack = Seq(ScriptNumberFactory.fromNumber(500000000))
|
||||
val script = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),0)
|
||||
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
|
||||
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,500000000)
|
||||
val baseProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
|
||||
val baseProgram : PreExecutionScriptProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
|
||||
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
|
||||
val program = ScriptProgram(baseProgram,stack,script)
|
||||
val newProgram = opCheckLockTimeVerify(program)
|
||||
newProgram.isValid must be (true)
|
||||
//if an error is hit, the newProgram will be an instance of ExecutedScriptProgram
|
||||
//if an error is not hit it will still be a ExecutionInProgressScriptProgram
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package org.scalacoin.script.stack
|
||||
|
||||
import org.scalacoin.script.error.ScriptErrorInvalidStackOperation
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.bitwise.OP_EQUAL
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.{BitcoinSUtil, TestUtil}
|
||||
import org.scalacoin.util.{ScriptProgramTestUtil, BitcoinSUtil, TestUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
|
@ -34,8 +35,8 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
it must "mark the script invalid when calling opDup without an element on top of the stack" in {
|
||||
val stack = List()
|
||||
val script = List(OP_DUP)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
opDup(program).isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
ScriptProgramTestUtil.toExecutedScriptProgram(opDup(program)).error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -111,9 +112,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(OP_0)
|
||||
val script = List(OP_NIP)
|
||||
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNip(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -121,9 +122,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List()
|
||||
val script = List(OP_NIP)
|
||||
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNip(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opNip(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "evaluate an OP_OVER correctly" in {
|
||||
|
@ -139,9 +140,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(OP_0)
|
||||
val script = List(OP_OVER)
|
||||
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opOver(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -149,9 +150,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List()
|
||||
val script = List(OP_OVER)
|
||||
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opOver(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opOver(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -166,10 +167,10 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
/* it must "evaluate an OP_ROLL correctly" in {
|
||||
it must "evaluate an OP_ROLL correctly" in {
|
||||
val stack = List(OP_0, ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
|
||||
val script = List(OP_ROLL)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opRoll(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("14"),
|
||||
|
@ -181,7 +182,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
it must "evaluate an OP_ROT correctly" in {
|
||||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
|
||||
val script = List(OP_ROT)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opRot(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("16"),ScriptConstantImpl("14"),ScriptConstantImpl("15")))
|
||||
|
@ -192,9 +193,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"))
|
||||
val script = List(OP_ROT)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opRot(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opRot(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,7 +203,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_2ROT)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Rot(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("18"),ScriptConstantImpl("19"),ScriptConstantImpl("14"),
|
||||
|
@ -210,23 +211,21 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "throw an exception if there is less than 6 elements on the stack for OP_2ROT" in {
|
||||
it must "mark a scirpt as invalid if there is less than 6 elements on the stack for OP_2ROT" in {
|
||||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"))
|
||||
val script = List(OP_2ROT)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
|
||||
intercept[IllegalArgumentException] {
|
||||
val newProgram = op2Rot(program)
|
||||
}
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Rot(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "evalauate an OP_2DROP correctly" in {
|
||||
it must "evaluate an OP_2DROP correctly" in {
|
||||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_2DROP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Drop(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("16"),
|
||||
|
@ -238,7 +237,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_SWAP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opSwap(program)
|
||||
newProgram.stack must be (List(ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")))
|
||||
|
@ -249,7 +248,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_TUCK)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opTuck(program)
|
||||
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("14"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")))
|
||||
|
@ -260,9 +259,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(OP_0)
|
||||
val script = List(OP_TUCK)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opTuck(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opTuck(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -270,7 +269,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_2DUP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Dup(program)
|
||||
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),
|
||||
ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
|
@ -282,9 +281,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(OP_0)
|
||||
val script = List(OP_2DUP)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Dup(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Dup(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
|
||||
}
|
||||
|
||||
|
@ -292,7 +291,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_3DUP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op3Dup(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("16"),
|
||||
|
@ -305,7 +304,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_2OVER)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Over(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("16"),
|
||||
|
@ -318,9 +317,9 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
|
||||
val script = List(OP_2OVER)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Over(program)
|
||||
newProgram.isValid must be (false)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Over(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
|
||||
|
@ -328,7 +327,7 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"),
|
||||
ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))
|
||||
val script = List(OP_2SWAP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val program = ScriptProgram(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Swap(program)
|
||||
|
||||
newProgram.stack must be (List(ScriptConstantImpl("16"), ScriptConstantImpl("17"),ScriptConstantImpl("14"),
|
||||
|
@ -340,8 +339,8 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
|
|||
val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"))
|
||||
val script = List(OP_2SWAP)
|
||||
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op2Swap(program)
|
||||
newProgram.isValid must be (false)
|
||||
}*/
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress, stack,script)
|
||||
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(op2Swap(program))
|
||||
newProgram.error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -6,7 +6,7 @@ import org.scalacoin.policy.Policy
|
|||
import org.scalacoin.protocol.script._
|
||||
import org.scalacoin.protocol.transaction.{EmptyTransaction, Transaction}
|
||||
import org.scalacoin.protocol.{AssetAddress, BitcoinAddress}
|
||||
import org.scalacoin.script.{ScriptProgram}
|
||||
import org.scalacoin.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
|
||||
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160}
|
||||
|
@ -142,6 +142,14 @@ object TestUtil {
|
|||
def testProgram : ScriptProgram = ScriptProgram(TransactionTestUtil.testTransaction,
|
||||
EmptyScriptPubKey,0,List(),Policy.standardScriptVerifyFlags)
|
||||
|
||||
def testProgramPreExecution = testProgram match {
|
||||
case p : PreExecutionScriptProgram => p
|
||||
case _ => throw new RuntimeException("this must be a script program that is pre execution")
|
||||
}
|
||||
|
||||
def testProgramExecutionInProgress = ScriptProgram.toExecutionInProgress(testProgramPreExecution)
|
||||
|
||||
|
||||
|
||||
val rawP2PKScriptSig = "47304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001"
|
||||
def p2pkScriptSig = ScriptSignature(rawP2PKScriptSig)
|
||||
|
@ -161,4 +169,9 @@ object TestUtil {
|
|||
ScriptConstantImpl("173014020002107777777777777777777777777777777701"),
|
||||
BytesToPushOntoStackImpl(33),
|
||||
ScriptConstantImpl("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652")))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue