diff --git a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala index b8e7f40451..dd903b9fd5 100644 --- a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala @@ -2,6 +2,7 @@ package org.scalacoin.script.interpreter import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey} import org.scalacoin.protocol.transaction.Transaction +import org.scalacoin.script.locktime.{OP_CHECKLOCKTIMEVERIFY, LockTimeInterpreter} import org.scalacoin.script.splice.{SpliceInterpreter, OP_SIZE} import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram} import org.scalacoin.script.arithmetic._ @@ -19,7 +20,8 @@ import scala.annotation.tailrec * Created by chris on 1/6/16. */ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with ControlOperationsInterpreter - with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter with SpliceInterpreter { + with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter with SpliceInterpreter + with LockTimeInterpreter { private def logger = LoggerFactory.getLogger(this.getClass().toString) @@ -132,6 +134,10 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con //splice operations case OP_SIZE :: t => loop(opSize(program)) + + //locktime operations + case OP_CHECKLOCKTIMEVERIFY :: t => loop(opCheckLockTimeVerify(program)) + //no more script operations to run, True is represented by any representation of non-zero case Nil => program.stack.headOption != Some(ScriptFalse) case h :: t => throw new RuntimeException(h + " was unmatched") diff --git a/src/main/scala/org/scalacoin/script/locktime/LockTimeInterpreter.scala b/src/main/scala/org/scalacoin/script/locktime/LockTimeInterpreter.scala new file mode 100644 index 0000000000..880183e9a0 --- /dev/null +++ b/src/main/scala/org/scalacoin/script/locktime/LockTimeInterpreter.scala @@ -0,0 +1,41 @@ +package org.scalacoin.script.locktime + +import org.scalacoin.script.constant.{ScriptNumberImpl, ScriptNumber} +import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram} + +/** + * Created by chris on 2/8/16. + */ +trait LockTimeInterpreter { + + + /** + * Marks transaction as invalid if the top stack item is greater than the transaction's nLockTime field, + * otherwise script evaluation continues as though an OP_NOP was executed. Transaction is also invalid if + * 1. the stack is empty; or + * 2. the top stack item is negative; or + * 3. the top stack item is greater than or equal to 500000000 while the transaction's nLockTime field is less than 500000000, + * or vice versa; or + * 4. the input's nSequence field is equal to 0xffffffff. + * The precise semantics are described in BIP 0065 + * @param program + * @return + */ + def opCheckLockTimeVerify(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_CHECKLOCKTIMEVERIFY, + "Script top must be OP_CHECKLOCKTIMEVERIFY") + if (program.stack.size == 0) { + ScriptProgramImpl(program.stack, program.script.tail, program.transaction, program.altStack,false) + } else { + val isValid = program.stack.head match { + case s : ScriptNumber if (s < ScriptNumberImpl(0)) => false + case s : ScriptNumber if (s > ScriptNumberImpl(500000000) && program.transaction.lockTime < 500000000) => false + case s : ScriptNumber if (s < ScriptNumberImpl(500000000) && program.transaction.lockTime > 500000000) => false + case s if (program.transaction.inputs.map(_.sequence == 0xffffffff).exists(_ == true)) => false + case _ => true + } + ScriptProgramImpl(program.stack, program.script.tail, program.transaction, program.altStack,isValid) + } + } + +}