mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-25 07:17:32 +01:00
Implementating base functionality for when OP_CSV should fail or be treated as a NOP
This commit is contained in:
parent
83bce905a1
commit
8b8b8dd34c
4 changed files with 113 additions and 10 deletions
|
@ -80,6 +80,10 @@ sealed trait ScriptNumber extends ScriptConstant {
|
|||
def > (that : ScriptNumber) : Boolean = num > that.num
|
||||
def >= (that : ScriptNumber) : Boolean = num >= that.num
|
||||
|
||||
def &(that : ScriptNumber) : ScriptNumber = ScriptNumber(num & that.num)
|
||||
|
||||
def | (that : ScriptNumber) : ScriptNumber = ScriptNumber(num | that.num)
|
||||
|
||||
/**
|
||||
* This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
|
||||
* are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true
|
||||
|
|
|
@ -2,9 +2,10 @@ package org.bitcoins.script.locktime
|
|||
|
||||
|
||||
import org.bitcoins.protocol.transaction.TransactionConstants
|
||||
import org.bitcoins.script.constant.{ScriptToken, ScriptNumber}
|
||||
import org.bitcoins.script.constant.{ScriptNumber, ScriptToken}
|
||||
import org.bitcoins.script.result._
|
||||
import org.bitcoins.script.{ScriptProgram}
|
||||
import org.bitcoins.script.ScriptProgram
|
||||
import org.bitcoins.script.flag.ScriptFlagUtil
|
||||
import org.bitcoins.util.BitcoinSLogger
|
||||
/**
|
||||
* Created by chris on 2/8/16.
|
||||
|
@ -29,10 +30,10 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKLOCKTIMEVERIFY,
|
||||
"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")
|
||||
logger.error("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because we have no stack items")
|
||||
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")
|
||||
logger.error("Transaction validation failing in OP_CHECKLOCKTIMEVERIFY because the sequence number is 0xffffffff")
|
||||
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
|
||||
}
|
||||
else {
|
||||
|
@ -46,11 +47,55 @@ trait LockTimeInterpreter extends BitcoinSLogger {
|
|||
case s : ScriptNumber if (s < ScriptNumber(500000000) && program.txSignatureComponent.transaction.lockTime >= 500000000) =>
|
||||
logger.warn("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
|
||||
Some(ScriptErrorUnsatisfiedLocktime)
|
||||
case _ : ScriptToken => None
|
||||
case _ : ScriptNumber => None
|
||||
case _ : ScriptToken => Some(ScriptErrorUnknownError)
|
||||
}
|
||||
if (isError.isDefined) ScriptProgram(program,isError.get)
|
||||
else ScriptProgram(program,program.stack, program.script.tail)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
|
||||
* 1.) the stack is empty; or
|
||||
* 2.) the top item on the stack is less than 0; or
|
||||
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
|
||||
* the transaction version is less than 2; or
|
||||
* the transaction input sequence number disable flag (1 << 31) is set; or
|
||||
* the relative lock-time type is not the same; or
|
||||
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
|
||||
* Otherwise, script execution will continue as if a NOP had been executed.
|
||||
* See BIP112 for more information
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opCheckSequenceVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
if (program.stack.isEmpty) {
|
||||
logger.error("Cannot execute OP_CHECKSEQUENCEVERIFY on an empty stack")
|
||||
ScriptProgram(program,ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
program.stack.head match {
|
||||
case ScriptNumber.negativeOne => ScriptProgram(program,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)
|
||||
case s : ScriptNumber if ((s.num & locktimeDisabledFlag) != 0) =>
|
||||
//see BIP68 for sematnic of locktimeDisableFalg
|
||||
logger.info("Locktime disable flag was set so OP_CHECKSEQUENCEVERIFY is treated as a NOP")
|
||||
ScriptProgram(program,program.script.tail,ScriptProgram.Script)
|
||||
case _ => ScriptProgram(program, program.stack.tail, program.script.tail)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If bit (1 << 31) of the sequence number is set,
|
||||
* then no consensus meaning is applied to the sequence number and can be included
|
||||
* in any block under all currently possible circumstances.
|
||||
* @return the mask that ben used with a bitwise and to indicate if the sequence number has any meaning
|
||||
*/
|
||||
def locktimeDisabledFlag = 1 << 31
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,23 @@ case object OP_CHECKLOCKTIMEVERIFY extends LocktimeOperation {
|
|||
override def opCode = 177
|
||||
}
|
||||
|
||||
/**
|
||||
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
|
||||
* 1.) the stack is empty; or
|
||||
* 2.) the top item on the stack is less than 0; or
|
||||
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
|
||||
* the transaction version is less than 2; or
|
||||
* the transaction input sequence number disable flag (1 << 31) is set; or
|
||||
* the relative lock-time type is not the same; or
|
||||
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
|
||||
* Otherwise, script execution will continue as if a NOP had been executed.
|
||||
* See BIP112 for more information
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
|
||||
*/
|
||||
case object OP_CHECKSEQUENCEVERIFY extends LocktimeOperation {
|
||||
override def opCode = 178
|
||||
}
|
||||
|
||||
object LocktimeOperation extends ScriptOperationFactory[LocktimeOperation] {
|
||||
override def operations = Seq(OP_CHECKLOCKTIMEVERIFY)
|
||||
override def operations = Seq(OP_CHECKLOCKTIMEVERIFY/*, OP_CHECKSEQUENCEVERIFY*/)
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package org.bitcoins.script.locktime
|
||||
|
||||
|
||||
import org.bitcoins.protocol.transaction.{TransactionInput, Transaction, UpdateTransactionInputs}
|
||||
import org.bitcoins.policy.Policy
|
||||
import org.bitcoins.protocol.transaction.{Transaction, TransactionInput, UpdateTransactionInputs}
|
||||
import org.bitcoins.script.result._
|
||||
import org.bitcoins.script.{ExecutionInProgressScriptProgram, ExecutedScriptProgram, PreExecutionScriptProgram, ScriptProgram}
|
||||
import org.bitcoins.script.constant.{ScriptNumber, OP_0}
|
||||
import org.bitcoins.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
|
||||
import org.bitcoins.script.constant.{OP_0, ScriptNumber}
|
||||
import org.bitcoins.util.{ScriptProgramTestUtil, TestUtil}
|
||||
import org.scalatest.{MustMatchers, FlatSpec}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
* Created by chris on 3/30/16.
|
||||
|
@ -97,5 +98,41 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
|
|||
//if an error is not hit it will still be a ExecutionInProgressScriptProgram
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
|
||||
}
|
||||
|
||||
it must "mark the script as invalid for OP_CHECKSEQUENCEVERIFY if there are no tokens on the stack" in {
|
||||
val stack = List()
|
||||
val script = List(OP_CHECKSEQUENCEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = opCheckSequenceVerify(program)
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (true)
|
||||
newProgram.asInstanceOf[ExecutedScriptProgram].error must be (Some(ScriptErrorInvalidStackOperation))
|
||||
}
|
||||
|
||||
it must "mark the script as invalid for OP_CHECKSEQUENCEVERIFY if the stack top is negative" in {
|
||||
val stack = List(ScriptNumber.negativeOne)
|
||||
val script = List(OP_CHECKSEQUENCEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = opCheckSequenceVerify(program)
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (true)
|
||||
newProgram.asInstanceOf[ExecutedScriptProgram].error must be (Some(ScriptErrorNegativeLockTime))
|
||||
}
|
||||
|
||||
it must "mark the script as invalid if we are requiring minimal encoding of numbers and the stack top is not minimal" in {
|
||||
val stack = List(ScriptNumber("0100"))
|
||||
val script = List(OP_CHECKSEQUENCEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = opCheckSequenceVerify(program)
|
||||
newProgram.isInstanceOf[ExecutedScriptProgram] must be (true)
|
||||
newProgram.asInstanceOf[ExecutedScriptProgram].error must be (Some(ScriptErrorUnknownError))
|
||||
}
|
||||
|
||||
it must "treat OP_CHECKSEQUENCEVERIFY as a NOP if the locktime disabled flag is set in the sequence number" in {
|
||||
val stack = List(ScriptNumber(locktimeDisabledFlag))
|
||||
val script = List(OP_CHECKSEQUENCEVERIFY)
|
||||
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script)
|
||||
val newProgram = opCheckSequenceVerify(program)
|
||||
newProgram.stack must be (stack)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue