mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 14:33:06 +01:00
* Script Program apply method refactor part 1 (#760) * Simplification of line * Typo fix
This commit is contained in:
parent
82e6c36493
commit
c1011cd8a7
10 changed files with 153 additions and 165 deletions
|
@ -6,8 +6,6 @@ import org.bitcoins.core.script.flag.ScriptFlag
|
|||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/**
|
||||
* Created by chris on 2/3/16.
|
||||
*/
|
||||
|
@ -47,6 +45,14 @@ sealed trait ScriptProgram {
|
|||
|
||||
/** Returns true if the stack top is false */
|
||||
def stackTopIsFalse: Boolean = !stackTopIsTrue
|
||||
|
||||
/**
|
||||
* Sets a [[org.bitcoins.core.script.result.ScriptError ScriptError]] on a given
|
||||
* [[org.bitcoins.core.script.ScriptProgram ScriptProgram]].
|
||||
* @param error the error that the program hit while being executed in the script interpreter
|
||||
* @return the ExecutedScriptProgram with the given error set inside of the trait
|
||||
*/
|
||||
def failExecution(error: ScriptError): ExecutedScriptProgram
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +67,11 @@ case class PreExecutionScriptProgram(
|
|||
originalScript: List[ScriptToken],
|
||||
altStack: List[ScriptToken],
|
||||
flags: Seq[ScriptFlag])
|
||||
extends ScriptProgram
|
||||
extends ScriptProgram {
|
||||
override def failExecution(error: ScriptError): ExecutedScriptProgram = {
|
||||
ScriptProgram.toExecutionInProgress(this).failExecution(error)
|
||||
}
|
||||
}
|
||||
|
||||
object PreExecutionScriptProgram {
|
||||
|
||||
|
@ -94,7 +104,11 @@ case class ExecutionInProgressScriptProgram(
|
|||
altStack: List[ScriptToken],
|
||||
flags: Seq[ScriptFlag],
|
||||
lastCodeSeparator: Option[Int])
|
||||
extends StartedScriptProgram
|
||||
extends StartedScriptProgram {
|
||||
override def failExecution(error: ScriptError): ExecutedScriptProgram = {
|
||||
ScriptProgram.toExecutedProgram(this).failExecution(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for a [[org.bitcoins.core.script.ScriptProgram ScriptProgram]] that has been
|
||||
|
@ -112,7 +126,11 @@ case class ExecutedScriptProgram(
|
|||
altStack: List[ScriptToken],
|
||||
flags: Seq[ScriptFlag],
|
||||
error: Option[ScriptError])
|
||||
extends StartedScriptProgram
|
||||
extends StartedScriptProgram {
|
||||
override def failExecution(error: ScriptError): ExecutedScriptProgram = {
|
||||
this.copy(error = Some(error))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory companion object for [[org.bitcoins.core.script.ScriptProgram ScriptProgram]]
|
||||
|
@ -126,37 +144,6 @@ object ScriptProgram extends BitcoinSLogger {
|
|||
case object AltStack extends UpdateIndicator
|
||||
case object OriginalScript extends UpdateIndicator
|
||||
|
||||
/**
|
||||
* Sets a [[org.bitcoins.core.script.result.ScriptError ScriptError]] on a given
|
||||
* [[org.bitcoins.core.script.ScriptProgram ScriptProgram]].
|
||||
* @param oldProgram the program who has hit an invalid state
|
||||
* @param error the error that the program hit while being executed in the script interpreter
|
||||
* @return the ExecutedScriptProgram with the given error set inside of the trait
|
||||
*/
|
||||
@tailrec
|
||||
def apply(
|
||||
oldProgram: ScriptProgram,
|
||||
error: ScriptError): ExecutedScriptProgram = oldProgram match {
|
||||
case program: PreExecutionScriptProgram =>
|
||||
ScriptProgram(ScriptProgram.toExecutionInProgress(program), error)
|
||||
case program: ExecutionInProgressScriptProgram =>
|
||||
ExecutedScriptProgram(program.txSignatureComponent,
|
||||
program.stack,
|
||||
program.script,
|
||||
program.originalScript,
|
||||
program.altStack,
|
||||
program.flags,
|
||||
Some(error))
|
||||
case program: ExecutedScriptProgram =>
|
||||
ExecutedScriptProgram(program.txSignatureComponent,
|
||||
program.stack,
|
||||
program.script,
|
||||
program.originalScript,
|
||||
program.altStack,
|
||||
program.flags,
|
||||
Some(error))
|
||||
}
|
||||
|
||||
def apply(
|
||||
oldProgram: PreExecutionScriptProgram,
|
||||
flags: Seq[ScriptFlag]): PreExecutionScriptProgram = {
|
||||
|
|
|
@ -131,7 +131,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
"Script top must be OP_NUMEQUALVERIFY")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val numEqualProgram = ScriptProgram(program,
|
||||
program.stack,
|
||||
|
@ -199,7 +199,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
"Script top must be OP_MIN")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
performComparisonOnTwoStackTopItems(
|
||||
program,
|
||||
|
@ -213,7 +213,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
"Script top must be OP_MAX")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
performComparisonOnTwoStackTopItems(
|
||||
program,
|
||||
|
@ -228,7 +228,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
"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, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val c = ScriptNumber(program.stack.head.bytes)
|
||||
val b = ScriptNumber(program.stack.tail.head.bytes)
|
||||
|
@ -239,7 +239,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
.isShortestEncoding(a))) {
|
||||
logger.error(
|
||||
"The constant you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (isLargerThan4Bytes(c) || isLargerThan4Bytes(b) || isLargerThan4Bytes(
|
||||
a)) {
|
||||
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
|
||||
|
@ -247,7 +247,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
logger.error(
|
||||
"Cannot perform arithmetic operation on a number larger than 4 bytes, one of these three numbers is larger than 4 bytes: "
|
||||
+ c + " " + b + " " + a)
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else {
|
||||
val isWithinRange = a >= b && a < c
|
||||
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
|
||||
|
@ -280,19 +280,19 @@ sealed abstract class ArithmeticInterpreter {
|
|||
case None =>
|
||||
logger.error(
|
||||
"We need one stack element for performing a unary arithmetic operation")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
case Some(s: ScriptNumber) =>
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil
|
||||
.isShortestEncoding(s)) {
|
||||
logger.error(
|
||||
"The number you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
} else if (isLargerThan4Bytes(s)) {
|
||||
logger.error(
|
||||
"Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
|
||||
//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)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else {
|
||||
val newScriptNumber = op(s)
|
||||
ScriptProgram(program,
|
||||
|
@ -304,7 +304,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
.isShortestEncoding(s)) {
|
||||
logger.error(
|
||||
"The number you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else {
|
||||
val interpretedNumber = ScriptNumber(ScriptNumberUtil.toLong(s.hex))
|
||||
val newProgram = ScriptProgram(
|
||||
|
@ -318,7 +318,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
|
||||
logger.error(
|
||||
"Stack top must be a script number/script constant to perform an arithmetic operation")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
if (program.stack.size < 2) {
|
||||
logger.error(
|
||||
"We must have two elements to perform a binary arithmetic operation")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
(program.stack.head, program.stack.tail.head) match {
|
||||
case (x: ScriptNumber, y: ScriptNumber) =>
|
||||
|
@ -344,13 +344,13 @@ sealed abstract class ArithmeticInterpreter {
|
|||
y))) {
|
||||
logger.error(
|
||||
"The constant you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (isLargerThan4Bytes(x) || isLargerThan4Bytes(y)) {
|
||||
//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, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else {
|
||||
val newStackTop = op(x, y)
|
||||
ScriptProgram(program,
|
||||
|
@ -386,7 +386,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
//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, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ sealed abstract class ArithmeticInterpreter {
|
|||
op: (ScriptNumber, ScriptNumber) => Boolean): StartedScriptProgram = {
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("We need two stack elements for a binary boolean operation")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val (x, y) = parseTopTwoStackElementsAsScriptNumbers(program)
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) &&
|
||||
|
@ -410,13 +410,13 @@ sealed abstract class ArithmeticInterpreter {
|
|||
.isShortestEncoding(y))) {
|
||||
logger.error(
|
||||
"The constant you gave us is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (isLargerThan4Bytes(x) || isLargerThan4Bytes(y)) {
|
||||
//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, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else {
|
||||
val newStackTop = if (op(x, y)) OP_TRUE else OP_FALSE
|
||||
ScriptProgram(program,
|
||||
|
|
|
@ -26,7 +26,7 @@ sealed abstract class BitwiseInterpreter {
|
|||
require(program.script.headOption.contains(OP_EQUAL),
|
||||
"Script operation must be OP_EQUAL")
|
||||
if (program.stack.size < 2) {
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val h = program.stack.head
|
||||
val h1 = program.stack.tail.head
|
||||
|
@ -67,13 +67,13 @@ sealed abstract class BitwiseInterpreter {
|
|||
|
||||
verifiedOrErr match {
|
||||
case p: ExecutedScriptProgram =>
|
||||
if (p.error.isDefined) ScriptProgram(p, ScriptErrorEqualVerify)
|
||||
if (p.error.isDefined) p.failExecution(ScriptErrorEqualVerify)
|
||||
else p
|
||||
case p: ExecutionInProgressScriptProgram => p
|
||||
}
|
||||
} else {
|
||||
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ sealed abstract class ConstantInterpreter {
|
|||
constant.isInstanceOf[ScriptNumber] && constant.toLong <= 16) {
|
||||
logger.error(
|
||||
"We can push this constant onto the stack with OP_0 - OP_16 instead of using a script constant")
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
} else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) {
|
||||
logger.error("Incorrect amount of bytes being pushed onto the stack")
|
||||
logger.error("Bytes needed: " + bytesNeeded)
|
||||
|
@ -102,13 +102,13 @@ sealed abstract class ConstantInterpreter {
|
|||
"Number of byte received: " + bytesToPushOntoStack
|
||||
.map(_.bytes.size)
|
||||
.sum)
|
||||
ScriptProgram(program, ScriptErrorBadOpCode)
|
||||
program.failExecution(ScriptErrorBadOpCode)
|
||||
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil
|
||||
.isMinimalPush(program.script.head, constant)) {
|
||||
logger.debug("Pushing operation: " + program.script.head)
|
||||
logger.debug("Constant parsed: " + constant)
|
||||
logger.debug("Constant size: " + constant.bytes.size)
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
} else ScriptProgram.apply(program, constant :: program.stack, newScript)
|
||||
}
|
||||
|
||||
|
@ -136,20 +136,20 @@ sealed abstract class ConstantInterpreter {
|
|||
logger.error(
|
||||
"We cannot use an OP_PUSHDATA operation for pushing " +
|
||||
"a script number operation onto the stack, scriptNumberOperation: " + scriptNumOp)
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && program.script.size > 2 &&
|
||||
!BitcoinScriptUtil.isMinimalPush(program.script.head,
|
||||
program.script(2))) {
|
||||
logger.error(
|
||||
"We are not using the minimal push operation to push the bytes onto the stack for the constant")
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
} else {
|
||||
//for the case where we have to push 0 bytes onto the stack, which is technically the empty byte vector
|
||||
program.script(1) match {
|
||||
case OP_0 | BytesToPushOntoStack.zero | ScriptNumber.zero |
|
||||
ScriptNumber.negativeZero =>
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags))
|
||||
ScriptProgram(program, ScriptErrorMinimalData)
|
||||
program.failExecution(ScriptErrorMinimalData)
|
||||
else
|
||||
ScriptProgram(program,
|
||||
ScriptNumber.zero :: program.stack,
|
||||
|
|
|
@ -31,13 +31,13 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
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, ScriptErrorUnbalancedConditional)
|
||||
program.failExecution(ScriptErrorUnbalancedConditional)
|
||||
} else if (program.stack.isEmpty) {
|
||||
logger.error("We do not have any stack elements for our OP_IF")
|
||||
ScriptProgram(program, ScriptErrorUnbalancedConditional)
|
||||
program.failExecution(ScriptErrorUnbalancedConditional)
|
||||
} else if (isNotMinimalStackTop(stackTop, sigVersion, minimalIfEnabled)) {
|
||||
logger.error("OP_IF argument was not minimally encoded, got: " + stackTop)
|
||||
ScriptProgram(program, ScriptErrorMinimalIf)
|
||||
program.failExecution(ScriptErrorMinimalIf)
|
||||
} else if (program.stackTopIsTrue) {
|
||||
logger.debug("OP_IF stack top was true")
|
||||
logger.debug("Stack top: " + program.stack)
|
||||
|
@ -85,7 +85,7 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
if (isNotMinimalStackTop(oldStackTop, sigVersion, minimalIfEnabled)) {
|
||||
//need to duplicate minimal check, we cannot accurately invert the stack
|
||||
//top for OP_IF otherwise
|
||||
ScriptProgram(program, ScriptErrorMinimalIf)
|
||||
program.failExecution(ScriptErrorMinimalIf)
|
||||
} else {
|
||||
val script = OP_IF :: program.script.tail
|
||||
val stackTop =
|
||||
|
@ -105,7 +105,7 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
"First script opt must be OP_ELSE")
|
||||
if (!program.script.tail.contains(OP_ENDIF)) {
|
||||
logger.error("OP_ELSE does not have a OP_ENDIF")
|
||||
ScriptProgram(program, ScriptErrorUnbalancedConditional)
|
||||
program.failExecution(ScriptErrorUnbalancedConditional)
|
||||
} else {
|
||||
val tree = parseBinaryTree(program.script)
|
||||
val treeWithNextOpElseRemoved = tree match {
|
||||
|
@ -129,7 +129,7 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
//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, ScriptErrorUnbalancedConditional)
|
||||
program.failExecution(ScriptErrorUnbalancedConditional)
|
||||
} else ScriptProgram(program, program.stack, program.script.tail)
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
def opReturn(
|
||||
program: ExecutionInProgressScriptProgram): StartedScriptProgram = {
|
||||
require(program.script.headOption.contains(OP_RETURN))
|
||||
ScriptProgram(program, ScriptErrorOpReturn)
|
||||
program.failExecution(ScriptErrorOpReturn)
|
||||
}
|
||||
|
||||
/** Marks [[org.bitcoins.core.protocol.transaction.Transaction Transaction]] as invalid if top stack value is not true. */
|
||||
|
@ -157,11 +157,11 @@ sealed abstract class ControlOperationsInterpreter {
|
|||
program.stack.nonEmpty match {
|
||||
case true =>
|
||||
logger.debug("Stack for OP_VERIFY: " + program.stack)
|
||||
if (program.stackTopIsFalse) ScriptProgram(program, ScriptErrorVerify)
|
||||
if (program.stackTopIsFalse) program.failExecution(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, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ sealed abstract class CryptoInterpreter {
|
|||
"Script top must be OP_CHECKSIG")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_CHECKSIG requires at lest two stack elements")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val pubKey = ECPublicKey(program.stack.head.bytes)
|
||||
val signature = ECDigitalSignature(program.stack.tail.head.bytes)
|
||||
|
@ -104,7 +104,7 @@ sealed abstract class CryptoInterpreter {
|
|||
"Script top must be OP_CHECKSIGVERIFY")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("Stack must contain at least 3 items for OP_CHECKSIGVERIFY")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val newScript = OP_CHECKSIG :: OP_VERIFY :: program.script.tail
|
||||
val newProgram = ScriptProgram(program, newScript, ScriptProgram.Script)
|
||||
|
@ -156,7 +156,7 @@ sealed abstract class CryptoInterpreter {
|
|||
|
||||
if (program.stack.size < 1) {
|
||||
logger.error("OP_CHECKMULTISIG requires at least 1 stack elements")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
//these next lines remove the appropriate stack/script values after the signatures have been checked
|
||||
val nPossibleSignatures: ScriptNumber =
|
||||
|
@ -164,14 +164,14 @@ sealed abstract class CryptoInterpreter {
|
|||
if (nPossibleSignatures < ScriptNumber.zero) {
|
||||
logger.error(
|
||||
"We cannot have the number of pubkeys in the script be negative")
|
||||
ScriptProgram(program, ScriptErrorPubKeyCount)
|
||||
program.failExecution(ScriptErrorPubKeyCount)
|
||||
} else if (ScriptFlagUtil.requireMinimalData(flags) && !nPossibleSignatures.isShortestEncoding) {
|
||||
logger.error(
|
||||
"The required signatures and the possible signatures must be encoded as the shortest number possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (program.stack.size < 2) {
|
||||
logger.error("We need at least 2 operations on the stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val mRequiredSignatures: ScriptNumber =
|
||||
BitcoinScriptUtil.numRequiredSignaturesOnStack(program)
|
||||
|
@ -179,13 +179,13 @@ sealed abstract class CryptoInterpreter {
|
|||
if (ScriptFlagUtil.requireMinimalData(flags) && !mRequiredSignatures.isShortestEncoding) {
|
||||
logger.error(
|
||||
"The required signatures val must be the shortest encoding as possible")
|
||||
return ScriptProgram(program, ScriptErrorUnknownError)
|
||||
return program.failExecution(ScriptErrorUnknownError)
|
||||
}
|
||||
|
||||
if (mRequiredSignatures < ScriptNumber.zero) {
|
||||
logger.error(
|
||||
"We cannot have the number of signatures specified in the script be negative")
|
||||
return ScriptProgram(program, ScriptErrorSigCount)
|
||||
return program.failExecution(ScriptErrorSigCount)
|
||||
}
|
||||
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
|
||||
val (pubKeysScriptTokens, stackWithoutPubKeys) =
|
||||
|
@ -214,22 +214,22 @@ sealed abstract class CryptoInterpreter {
|
|||
if (pubKeys.size > Consensus.maxPublicKeysPerMultiSig) {
|
||||
logger.error(
|
||||
"We have more public keys than the maximum amount of public keys allowed")
|
||||
ScriptProgram(program, ScriptErrorPubKeyCount)
|
||||
program.failExecution(ScriptErrorPubKeyCount)
|
||||
} else if (signatures.size > pubKeys.size) {
|
||||
logger.error(
|
||||
"We have more signatures than public keys inside OP_CHECKMULTISIG")
|
||||
ScriptProgram(program, ScriptErrorSigCount)
|
||||
program.failExecution(ScriptErrorSigCount)
|
||||
} else if (stackWithoutPubKeysAndSignatures.size < 1) {
|
||||
logger.error(
|
||||
"OP_CHECKMULTISIG must have a remaining element on the stack afterk execution")
|
||||
//this is because of a bug in bitcoin core for the implementation of OP_CHECKMULTISIG
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L966
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else if (ScriptFlagUtil.requireNullDummy(flags) &&
|
||||
(stackWithoutPubKeysAndSignatures.nonEmpty && stackWithoutPubKeysAndSignatures.head.bytes.nonEmpty)) {
|
||||
logger.error(
|
||||
"Script flag null dummy was set however the first element in the script signature was not an OP_0, stackWithoutPubKeysAndSignatures: " + stackWithoutPubKeysAndSignatures)
|
||||
ScriptProgram(program, ScriptErrorSigNullDummy)
|
||||
program.failExecution(ScriptErrorSigNullDummy)
|
||||
} else {
|
||||
//remove the last OP_CODESEPARATOR
|
||||
val removedOpCodeSeparatorsScript =
|
||||
|
@ -261,7 +261,7 @@ sealed abstract class CryptoInterpreter {
|
|||
if (program.stack.size < 3) {
|
||||
logger.error(
|
||||
"Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
|
||||
val newProgram = ScriptProgram(program, newScript, ScriptProgram.Script)
|
||||
|
@ -295,7 +295,7 @@ sealed abstract class CryptoInterpreter {
|
|||
} else {
|
||||
logger.error(
|
||||
"We must have the stack top defined to execute a hash function")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,25 +313,25 @@ sealed abstract class CryptoInterpreter {
|
|||
//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, ScriptErrorSigDer)
|
||||
program.failExecution(ScriptErrorSigDer)
|
||||
case SignatureValidationErrorIncorrectSignatures =>
|
||||
//this means that signature verification failed, however all signatures were encoded correctly
|
||||
//just push a OP_FALSE onto the stack
|
||||
ScriptProgram(program, OP_FALSE +: restOfStack, program.script.tail)
|
||||
case SignatureValidationErrorSignatureCount =>
|
||||
//means that we did not have enough signatures for OP_CHECKMULTISIG
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
case SignatureValidationErrorPubKeyEncoding =>
|
||||
//means that a public key was not encoded correctly
|
||||
ScriptProgram(program, ScriptErrorPubKeyType)
|
||||
program.failExecution(ScriptErrorPubKeyType)
|
||||
case SignatureValidationErrorHighSValue =>
|
||||
ScriptProgram(program, ScriptErrorSigHighS)
|
||||
program.failExecution(ScriptErrorSigHighS)
|
||||
case SignatureValidationErrorHashType =>
|
||||
ScriptProgram(program, ScriptErrorSigHashType)
|
||||
program.failExecution(ScriptErrorSigHashType)
|
||||
case SignatureValidationErrorWitnessPubKeyType =>
|
||||
ScriptProgram(program, ScriptErrorWitnessPubKeyType)
|
||||
program.failExecution(ScriptErrorWitnessPubKeyType)
|
||||
case SignatureValidationErrorNullFail =>
|
||||
ScriptProgram(program, ScriptErrorSigNullFail)
|
||||
program.failExecution(ScriptErrorSigNullFail)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
|
||||
val executedProgram: ExecutedScriptProgram =
|
||||
programFlagsViolated(program) match {
|
||||
case Some(err) => ScriptProgram(program, err)
|
||||
case Some(err) => program.failExecution(err)
|
||||
case None =>
|
||||
val scriptSigExecutedProgram = executeProgram(program)
|
||||
logger.trace(s"scriptSigExecutedProgram $scriptSigExecutedProgram")
|
||||
|
@ -210,7 +210,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
.isPushOnly(s.asm)) {
|
||||
logger.error(
|
||||
"p2sh redeem script must be push only operations whe SIGPUSHONLY flag is set")
|
||||
ScriptProgram(p2shRedeemScriptProgram, ScriptErrorSigPushOnly)
|
||||
p2shRedeemScriptProgram.failExecution(ScriptErrorSigPushOnly)
|
||||
} else executeProgram(p2shRedeemScriptProgram)
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
//need to check if the scriptSig is push only as required by bitcoin core
|
||||
//https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1419
|
||||
if (!BitcoinScriptUtil.isPushOnly(scriptSigAsm)) {
|
||||
ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorSigPushOnly)
|
||||
scriptPubKeyExecutedProgram.failExecution(ScriptErrorSigPushOnly)
|
||||
} else if (scriptPubKeyExecutedProgram.error.isDefined) {
|
||||
scriptPubKeyExecutedProgram
|
||||
} else {
|
||||
|
@ -256,8 +256,8 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
isExpectedScriptBytes) {
|
||||
executeSegWitScript(scriptPubKeyExecutedProgram, p2wpkh).get
|
||||
} else if (segwitEnabled) {
|
||||
ScriptProgram(oldProgram = scriptPubKeyExecutedProgram,
|
||||
error = ScriptErrorWitnessMalleatedP2SH)
|
||||
scriptPubKeyExecutedProgram.failExecution(
|
||||
ScriptErrorWitnessMalleatedP2SH)
|
||||
} else {
|
||||
//segwit not enabled, treat as old spk
|
||||
run(scriptPubKeyExecutedProgram, p2wpkh)
|
||||
|
@ -283,8 +283,8 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
logger.error("ScriptSig bytes: " + scriptSig.hex)
|
||||
logger.error(
|
||||
"expected scriptsig bytes: " + expectedScriptBytes.toHex)
|
||||
ScriptProgram(scriptPubKeyExecutedProgram,
|
||||
ScriptErrorWitnessMalleatedP2SH)
|
||||
scriptPubKeyExecutedProgram.failExecution(
|
||||
ScriptErrorWitnessMalleatedP2SH)
|
||||
} else {
|
||||
logger.warn(
|
||||
"redeem script was witness script pubkey, segwit was NOT enabled")
|
||||
|
@ -327,15 +327,15 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
logger.error(
|
||||
"Cannot verify witness program with a BaseTxSigComponent")
|
||||
Success(
|
||||
ScriptProgram(scriptPubKeyExecutedProgram,
|
||||
ScriptErrorWitnessProgramWitnessEmpty))
|
||||
scriptPubKeyExecutedProgram.failExecution(
|
||||
ScriptErrorWitnessProgramWitnessEmpty))
|
||||
case UnassignedWitness(_) =>
|
||||
evaluateUnassignedWitness(b)
|
||||
}
|
||||
case (_, _) =>
|
||||
Success(
|
||||
ScriptProgram(scriptPubKeyExecutedProgram,
|
||||
ScriptErrorWitnessMalleated))
|
||||
scriptPubKeyExecutedProgram.failExecution(
|
||||
ScriptErrorWitnessMalleated))
|
||||
}
|
||||
case w: WitnessTxSigComponent =>
|
||||
val scriptSig =
|
||||
|
@ -350,14 +350,16 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
case (EmptyScriptSignature, _) | (_, _: P2SHScriptPubKey) =>
|
||||
if (witness.stack.exists(_.size > MAX_PUSH_SIZE)) {
|
||||
Success(
|
||||
ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorPushSize))
|
||||
scriptPubKeyExecutedProgram.failExecution(ScriptErrorPushSize)
|
||||
)
|
||||
} else {
|
||||
verifyWitnessProgram(witnessVersion, witness, witnessProgram, w)
|
||||
}
|
||||
case (_, _) =>
|
||||
Success(
|
||||
ScriptProgram(scriptPubKeyExecutedProgram,
|
||||
ScriptErrorWitnessMalleated))
|
||||
scriptPubKeyExecutedProgram.failExecution(
|
||||
ScriptErrorWitnessMalleated)
|
||||
)
|
||||
}
|
||||
case _: WitnessTxSigComponentRebuilt =>
|
||||
Failure(new IllegalArgumentException(
|
||||
|
@ -381,7 +383,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
logger.trace("Stack after evaluating witness: " + evaluated.stack)
|
||||
if (evaluated.error.isDefined) evaluated
|
||||
else if (evaluated.stack.size != 1 || evaluated.stackTopIsFalse)
|
||||
ScriptProgram(evaluated, ScriptErrorEvalFalse)
|
||||
evaluated.failExecution(ScriptErrorEvalFalse)
|
||||
else evaluated
|
||||
}
|
||||
|
||||
|
@ -428,8 +430,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
val scriptByteVector = BitcoinSUtil.toByteVector(program.script)
|
||||
if (scriptByteVector.length > 10000) {
|
||||
logger.error("We cannot run a script that is larger than 10,000 bytes")
|
||||
ScriptProgram(ScriptProgram.toExecutionInProgress(program),
|
||||
ScriptErrorScriptSize)
|
||||
program.failExecution(ScriptErrorScriptSize)
|
||||
} else {
|
||||
loop(ScriptProgram.toExecutionInProgress(program, Some(program.stack)), 0)
|
||||
}
|
||||
|
@ -446,7 +447,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
logger.trace("Counted ops: " + countedOps)
|
||||
|
||||
if (countedOps > MAX_SCRIPT_OPS && program.error.isEmpty) {
|
||||
completeProgramExecution(ScriptProgram(program, ScriptErrorOpCount))
|
||||
completeProgramExecution(program.failExecution(ScriptErrorOpCount))
|
||||
} else {
|
||||
program
|
||||
}
|
||||
|
@ -472,10 +473,10 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
"We have reached the maximum amount of script operations allowed")
|
||||
logger.error(
|
||||
"Here are the remaining operations in the script: " + program.script)
|
||||
completeProgramExecution(ScriptProgram(program, ScriptErrorOpCount))
|
||||
completeProgramExecution(program.failExecution(ScriptErrorOpCount))
|
||||
} else if (scriptByteVector.length > 10000) {
|
||||
logger.error("We cannot run a script that is larger than 10,000 bytes")
|
||||
completeProgramExecution(ScriptProgram(program, ScriptErrorScriptSize))
|
||||
completeProgramExecution(program.failExecution(ScriptErrorScriptSize))
|
||||
} else {
|
||||
val (nextProgram, nextOpCount) = program.script match {
|
||||
//if at any time we see that the program is not valid
|
||||
|
@ -484,7 +485,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
if program.script.intersect(Seq(OP_VERIF, OP_VERNOTIF)).nonEmpty =>
|
||||
logger.error(
|
||||
"Script is invalid even when a OP_VERIF or OP_VERNOTIF occurs in an unexecuted OP_IF branch")
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode), opCount)
|
||||
(program.failExecution(ScriptErrorBadOpCode), opCount)
|
||||
//disabled splice operation
|
||||
case _
|
||||
if program.script
|
||||
|
@ -492,7 +493,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
.nonEmpty =>
|
||||
logger.error(
|
||||
"Script is invalid because it contains a disabled splice operation")
|
||||
(ScriptProgram(program, ScriptErrorDisabledOpCode), opCount)
|
||||
(program.failExecution(ScriptErrorDisabledOpCode), opCount)
|
||||
//disabled bitwise operations
|
||||
case _
|
||||
if program.script
|
||||
|
@ -500,7 +501,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
.nonEmpty =>
|
||||
logger.error(
|
||||
"Script is invalid because it contains a disabled bitwise operation")
|
||||
(ScriptProgram(program, ScriptErrorDisabledOpCode), opCount)
|
||||
(program.failExecution(ScriptErrorDisabledOpCode), opCount)
|
||||
//disabled arithmetic operations
|
||||
case _
|
||||
if program.script
|
||||
|
@ -515,19 +516,19 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
.nonEmpty =>
|
||||
logger.error(
|
||||
"Script is invalid because it contains a disabled arithmetic operation")
|
||||
(ScriptProgram(program, ScriptErrorDisabledOpCode), opCount)
|
||||
(program.failExecution(ScriptErrorDisabledOpCode), opCount)
|
||||
//program cannot contain a push operation > 520 bytes
|
||||
case _
|
||||
if program.script.exists(token =>
|
||||
token.bytes.size > MAX_PUSH_SIZE) =>
|
||||
logger.error(
|
||||
"We have a script constant that is larger than 520 bytes, this is illegal: " + program.script)
|
||||
(ScriptProgram(program, ScriptErrorPushSize), opCount)
|
||||
(program.failExecution(ScriptErrorPushSize), opCount)
|
||||
//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")
|
||||
(ScriptProgram(program, ScriptErrorStackSize), opCount)
|
||||
(program.failExecution(ScriptErrorStackSize), opCount)
|
||||
|
||||
//stack operations
|
||||
case OP_DUP :: _ =>
|
||||
|
@ -960,7 +961,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
if ScriptFlagUtil.discourageUpgradableNOPs(program.flags) =>
|
||||
logger.error(
|
||||
"We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
(ScriptProgram(program, ScriptErrorDiscourageUpgradableNOPs),
|
||||
(program.failExecution(ScriptErrorDiscourageUpgradableNOPs),
|
||||
calcOpCount(opCount, nop))
|
||||
case (nop: NOP) :: t =>
|
||||
val programOrError = ScriptProgram(program, program.stack, t)
|
||||
|
@ -970,25 +971,25 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
|
||||
case OP_RESERVED :: _ =>
|
||||
logger.error("OP_RESERVED automatically marks transaction invalid")
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode),
|
||||
(program.failExecution(ScriptErrorBadOpCode),
|
||||
calcOpCount(opCount, OP_RESERVED))
|
||||
case OP_VER :: _ =>
|
||||
logger.error("Transaction is invalid when executing OP_VER")
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode),
|
||||
(program.failExecution(ScriptErrorBadOpCode),
|
||||
calcOpCount(opCount, OP_VER))
|
||||
case OP_RESERVED1 :: _ =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED1")
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode),
|
||||
(program.failExecution(ScriptErrorBadOpCode),
|
||||
calcOpCount(opCount, OP_RESERVED1))
|
||||
case OP_RESERVED2 :: _ =>
|
||||
logger.error("Transaction is invalid when executing OP_RESERVED2")
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode),
|
||||
(program.failExecution(ScriptErrorBadOpCode),
|
||||
calcOpCount(opCount, OP_RESERVED2))
|
||||
|
||||
case (reservedOperation: ReservedOperation) :: _ =>
|
||||
logger.error(
|
||||
"Undefined operation found which automatically fails the script: " + reservedOperation)
|
||||
(ScriptProgram(program, ScriptErrorBadOpCode),
|
||||
(program.failExecution(ScriptErrorBadOpCode),
|
||||
calcOpCount(opCount, reservedOperation))
|
||||
//splice operations
|
||||
case OP_SIZE :: _ =>
|
||||
|
@ -1011,7 +1012,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
else if (ScriptFlagUtil.discourageUpgradableNOPs(program.flags)) {
|
||||
logger.error(
|
||||
"We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
(ScriptProgram(program, ScriptErrorDiscourageUpgradableNOPs),
|
||||
(program.failExecution(ScriptErrorDiscourageUpgradableNOPs),
|
||||
calcOpCount(opCount, OP_CHECKLOCKTIMEVERIFY))
|
||||
} //in this case, just reat OP_CLTV just like a NOP and remove it from the stack
|
||||
else {
|
||||
|
@ -1035,7 +1036,7 @@ sealed abstract class ScriptInterpreter extends BitcoinSLogger {
|
|||
else if (ScriptFlagUtil.discourageUpgradableNOPs(program.flags)) {
|
||||
logger.error(
|
||||
"We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
|
||||
(ScriptProgram(program, ScriptErrorDiscourageUpgradableNOPs),
|
||||
(program.failExecution(ScriptErrorDiscourageUpgradableNOPs),
|
||||
calcOpCount(opCount, OP_CHECKSEQUENCEVERIFY))
|
||||
} //in this case, just read OP_CSV just like a NOP and remove it from the stack
|
||||
else {
|
||||
|
|
|
@ -46,46 +46,46 @@ sealed abstract class LockTimeInterpreter {
|
|||
if (program.stack.size == 0) {
|
||||
logger.error(
|
||||
"Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because we have no stack items")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else if (input.sequence == TransactionConstants.sequence) {
|
||||
logger.error(
|
||||
"Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because the sequence number is 0xffffffff")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
} else {
|
||||
program.stack.head match {
|
||||
case s: ScriptNumber if (s < ScriptNumber.zero) =>
|
||||
logger.error(
|
||||
"OP_CHECKLOCKTIMEVERIFY marks tx as invalid if the stack top is negative")
|
||||
ScriptProgram(program, ScriptErrorNegativeLockTime)
|
||||
program.failExecution(ScriptErrorNegativeLockTime)
|
||||
case s: ScriptNumber
|
||||
if (s >= ScriptNumber(500000000) && transaction.lockTime < UInt32(
|
||||
500000000)) =>
|
||||
logger.error(
|
||||
"OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top >= 500000000 & tx locktime < 500000000")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
case s: ScriptNumber
|
||||
if (s < ScriptNumber(500000000) && transaction.lockTime >= UInt32(
|
||||
500000000)) =>
|
||||
logger.error(
|
||||
"OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
case s: ScriptNumber =>
|
||||
if (s.bytes.size > 5) {
|
||||
//if the number size is larger than 5 bytes the number is invalid
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (checkLockTime(program, s)) {
|
||||
ScriptProgram(program, program.script.tail, ScriptProgram.Script)
|
||||
} else {
|
||||
logger.error(
|
||||
"Stack top locktime and transaction locktime number comparison failed")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
}
|
||||
case s: ScriptConstant =>
|
||||
opCheckLockTimeVerify(
|
||||
ScriptProgram(program,
|
||||
ScriptNumber(s.hex) :: program.stack.tail,
|
||||
ScriptProgram.Stack))
|
||||
case _: ScriptToken => ScriptProgram(program, ScriptErrorUnknownError)
|
||||
case _: ScriptToken => program.failExecution(ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,16 +107,16 @@ sealed abstract class LockTimeInterpreter {
|
|||
program: ExecutionInProgressScriptProgram): StartedScriptProgram = {
|
||||
if (program.stack.isEmpty) {
|
||||
logger.error("Cannot execute OP_CHECKSEQUENCEVERIFY on an empty stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
program.stack.head match {
|
||||
case ScriptNumber.negativeOne =>
|
||||
ScriptProgram(program, ScriptErrorNegativeLockTime)
|
||||
program.failExecution(ScriptErrorNegativeLockTime)
|
||||
case s: ScriptNumber
|
||||
if (ScriptFlagUtil.requireMinimalData(program.flags) && !s.isShortestEncoding) =>
|
||||
logger.error(
|
||||
"Sequence number is not encoded in the shortest way possible")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
case s: ScriptNumber if (!isLockTimeBitOff(s)) =>
|
||||
//see BIP68 for semantic of locktimeDisableFlag
|
||||
logger.info(
|
||||
|
@ -126,17 +126,17 @@ sealed abstract class LockTimeInterpreter {
|
|||
if (isLockTimeBitOff(s) && program.txSignatureComponent.transaction.version < TransactionConstants.validLockVersion) =>
|
||||
logger.error(
|
||||
"OP_CSV fails if locktime bit is not set and the tx version < 2")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
case s: ScriptNumber =>
|
||||
if (s.bytes.size > 5) {
|
||||
//if the number size is larger than 5 bytes the number is invalid
|
||||
logger.error(
|
||||
"The OP_CSV value in the script was larger than 5 bytes in size.")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
} else if (checkSequence(program, s)) {
|
||||
ScriptProgram(program, program.stack, program.script.tail)
|
||||
} else {
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
program.failExecution(ScriptErrorUnsatisfiedLocktime)
|
||||
}
|
||||
case s: ScriptConstant =>
|
||||
opCheckSequenceVerify(
|
||||
|
|
|
@ -35,7 +35,7 @@ sealed abstract class SpliceInterpreter {
|
|||
}
|
||||
} else {
|
||||
logger.error("Must have at least 1 element on the stack for OP_SIZE")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, h :: program.stack, program.script.tail)
|
||||
case Nil =>
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ sealed abstract class StackInterpreter {
|
|||
program.script.tail)
|
||||
} else {
|
||||
logger.error("Cannot duplicate the top element on an empty stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ sealed abstract class StackInterpreter {
|
|||
program.stack.head :: program.altStack)
|
||||
} else {
|
||||
logger.error("OP_TOALTSTACK requires an element to be on the stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ sealed abstract class StackInterpreter {
|
|||
} else {
|
||||
logger.error(
|
||||
"Alt Stack must have at least one item on it for OP_FROMALTSTACK")
|
||||
ScriptProgram(program, ScriptErrorInvalidAltStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidAltStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, program.stack.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("Stack must have at least one item on it for OP_DROP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,10 +117,10 @@ sealed abstract class StackInterpreter {
|
|||
case h :: _ :: t => ScriptProgram(program, h :: t, program.script.tail)
|
||||
case _ :: _ =>
|
||||
logger.error("Stack must have at least two items on it for OP_NIP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
case Nil =>
|
||||
logger.error("Stack must have at least two items on it for OP_NIP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,10 +134,10 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, h1 :: program.stack, program.script.tail)
|
||||
case _ :: _ =>
|
||||
logger.error("Stack must have at least two items on it for OP_OVER")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
case Nil =>
|
||||
logger.error("Stack must have at least two items on it for OP_OVER")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ sealed abstract class StackInterpreter {
|
|||
program, { number: ScriptNumber =>
|
||||
//check if n is within the bound of the script
|
||||
if (program.stack.size < 2)
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
else if (number.toLong >= 0 && number.toLong < program.stack.tail.size) {
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
ScriptProgram(program,
|
||||
|
@ -159,7 +159,7 @@ sealed abstract class StackInterpreter {
|
|||
} else {
|
||||
logger.error(
|
||||
"The index for OP_PICK would have caused an index out of bounds exception")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -174,7 +174,7 @@ sealed abstract class StackInterpreter {
|
|||
program,
|
||||
(number: ScriptNumber) =>
|
||||
if (program.stack.size < 2)
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
else if (number.toLong >= 0 && number.toLong < program.stack.tail.size) {
|
||||
val newStackTop = program.stack.tail(number.toInt)
|
||||
//removes the old instance of the stack top, appends the new index to the head
|
||||
|
@ -184,7 +184,7 @@ sealed abstract class StackInterpreter {
|
|||
} else {
|
||||
logger.error(
|
||||
"The index for OP_ROLL would have caused an index out of bounds exception")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 3 items on it for OP_ROT")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("OP_2ROT requires 6 elements on the stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,7 +233,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
|
||||
} else {
|
||||
logger.error("OP_2DROP requires two elements to be on the stack")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
} else {
|
||||
logger.error("Stack must have at least 2 items on it for OP_SWAP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_TUCK")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 2 items on it for OP_2DUP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 3 items on it for OP_3DUP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,7 +307,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 4 items on it for OP_2OVER")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +322,7 @@ sealed abstract class StackInterpreter {
|
|||
ScriptProgram(program, newStack, program.script.tail)
|
||||
case _ =>
|
||||
logger.error("Stack must have at least 4 items on it for OP_2SWAP")
|
||||
ScriptProgram(program, ScriptErrorInvalidStackOperation)
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ sealed abstract class StackInterpreter {
|
|||
case Success(n) => op(n)
|
||||
case Failure(_) =>
|
||||
logger.error("Script number was not minimally encoded")
|
||||
ScriptProgram(program, ScriptErrorUnknownError)
|
||||
program.failExecution(ScriptErrorUnknownError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue