Fixing bug in pushing constants onto the stack whose op codes are ScriptNumberOperations -- for instance pushing an 83 byte transaction (whose op code is OP_3) -- whose script looks like OP_PUSHDATA1 OP_3 <83 byte tx> would fail before this

This commit is contained in:
Chris Stewart 2017-03-08 10:59:20 -06:00
parent d8ee3ddb61
commit 3835148f05
2 changed files with 23 additions and 10 deletions

View File

@ -37,7 +37,6 @@ trait ConstantInterpreter extends BitcoinSLogger {
bytesNeededForPushOp(program.script(1))
case _ : ScriptToken => bytesNeededForPushOp(program.script.head)
}
/** Parses the script tokens that need to be pushed onto our stack. */
@tailrec
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], accum : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
@ -56,8 +55,8 @@ trait ConstantInterpreter extends BitcoinSLogger {
}
val (newScript,bytesToPushOntoStack) = program.script.head match {
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 => takeUntilBytesNeeded(program.script.tail.tail, List())
case _: ScriptToken => takeUntilBytesNeeded(program.script.tail, List())
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 => takeUntilBytesNeeded(program.script.tail.tail, Nil)
case _: ScriptToken => takeUntilBytesNeeded(program.script.tail, Nil)
}
logger.debug("new script: " + newScript)
logger.debug("Bytes to push onto stack: " + bytesToPushOntoStack)
@ -73,13 +72,16 @@ trait ConstantInterpreter extends BitcoinSLogger {
logger.error("We can push this constant onto the stack with OP_0 - OP_16 instead of using a script constant")
ScriptProgram(program,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)
logger.error("Number of byte received: " + bytesToPushOntoStack.map(_.bytes.size).sum)
ScriptProgram(program,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)
logger.debug("Pushing operation: " + program.script.head)
logger.debug("Constant parsed: " + constant)
logger.debug("Constant size: " + constant.bytes.size)
ScriptProgram(program,ScriptErrorMinimalData)
} else ScriptProgram.apply(program, constant :: program.stack, newScript)
}
@ -90,7 +92,7 @@ trait ConstantInterpreter extends BitcoinSLogger {
//constant telling OP_PUSHDATA how many bytes need to go onto the stack
//for instance OP_PUSHDATA1 OP_0
val scriptNumOp = program.script(1).bytes match {
case h :: t => ScriptNumberOperation(h)
case h :: t => ScriptNumberOperation.fromNumber(h.toInt)
case Nil => None
}
if (ScriptFlagUtil.requireMinimalData(program.flags) && program.script(1).bytes.size == 1 &&
@ -117,6 +119,7 @@ trait ConstantInterpreter extends BitcoinSLogger {
/** Parses the bytes needed for a push op (for instance OP_PUSHDATA1). */
private def bytesNeededForPushOp(token : ScriptToken) : Long = token match {
case scriptNumber: BytesToPushOntoStack => scriptNumber.opCode
case scriptNumOp: ScriptNumberOperation => scriptNumOp.opCode
case scriptNumber: ScriptNumber => scriptNumber.underlying
case scriptConstant : ScriptConstant =>
val constantFlippedEndianness = BitcoinSUtil.flipEndianness(scriptConstant.hex)

View File

@ -127,12 +127,22 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
}
}
it must "return ScriptErrorMinimalData if program contains ScriptVerifyMinimalData flag and 2nd item in script is" +
" zero" in {
it must "return ScriptErrorMinimalData if program contains ScriptVerifyMinimalData flag and 2nd item in script is zero" in {
val stack = List()
val script = List(OP_PUSHDATA4,ScriptNumber.zero)
val program = ScriptProgram(ScriptProgram(TestUtil.testProgram, stack,script),Seq[ScriptFlag](ScriptVerifyMinimalData))
val newProgram = ScriptProgramTestUtil.toExecutedScriptProgram(opPushData4(program))
newProgram.error must be (Some(ScriptErrorMinimalData))
}
it must "push a constant onto the stack that is using OP_PUSHDATA1 where the pushop can be interpreted as a script number operation" in {
val constant = ScriptConstant("01000000010000000000000000000000000000000000000000000000000000000000000000" +
"ffffffff00ffffffff014a7afa8f7d52fd9e17a914b167f19394cd656c34f843ac2387e602007fd15b8700000000")
val stack = Nil
val script = List(OP_PUSHDATA1, OP_3, constant)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val newProgram = opPushData1(program)
newProgram.stack must be (Seq(constant))
}
}