Refactoring where OP_CODESEPARATORs are removed, this is now done inside of opCheckSig/opCheckMultiSig. Also passing the script that needs to be serialized to TransactionSignatureChecker now

This commit is contained in:
Chris Stewart 2016-05-11 20:43:15 -05:00
parent 7e80094ee1
commit ae117f9ffb
11 changed files with 285 additions and 280 deletions

View file

@ -4,7 +4,7 @@ import org.bitcoins.config.TestNet3
import org.bitcoins.protocol.script._
import org.bitcoins.protocol.transaction.{Transaction, TransactionInput}
import org.bitcoins.script.ScriptProgram
import org.bitcoins.script.constant.ScriptConstant
import org.bitcoins.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.script.crypto._
import org.bitcoins.script.flag.{ScriptFlag, ScriptFlagUtil, ScriptVerifyDerSig}
import org.bitcoins.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
@ -20,14 +20,17 @@ import scala.annotation.tailrec
trait TransactionSignatureChecker extends BitcoinSLogger {
/**
* Checks the signature of a scriptSig in the spending transaction against the
* given scriptPubKey & explicitly given public key
* This is useful for instances of non standard scriptSigs
* @param txSignatureComponent the tx signature component that contains all relevant tx information
* @param pubKey
* @return
*/
def checkSignature(txSignatureComponent : TransactionSignatureComponent,
* Checks the signature of a scriptSig in the spending transaction against the
* given scriptPubKey & explicitly given public key
* This is useful for instances of non standard scriptSigs
* @param txSignatureComponent the relevant transaction information for signature checking
* @param script the current script state inside the interpreter - this is needed in the case of OP_CODESEPARATORS
* @param pubKey the public key the signature is being checked against
* @param signature the signature which is being checked against the transaction & the public key
* @param flags the script flags used to check validity of the signature
* @return a boolean indicating if the signature is valid or not
*/
def checkSignature(txSignatureComponent : TransactionSignatureComponent, script : Seq[ScriptToken],
pubKey: ECPublicKey, signature : ECDigitalSignature, flags : Seq[ScriptFlag]) : TransactionSignatureCheckerResult = {
logger.info("Signature: " + signature)
val pubKeyEncodedCorrectly = BitcoinScriptUtil.checkPubKeyEncoding(pubKey,flags)
@ -58,22 +61,26 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | EmptyScriptSignature =>
if (txSignatureComponent.scriptPubKey.asm.contains(ScriptConstant(signature.hex))) {
logger.debug("Script before sigRemoved: " + script)
logger.debug("Signature: " + signature)
logger.debug("PubKey: " + pubKey)
val sigRemoved = if (script.contains(ScriptConstant(signature.hex))) {
//replicates this line in bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L872
val sigIndex = txSignatureComponent.scriptPubKey.asm.indexOf(ScriptConstant(signature.hex))
val sigIndex = script.indexOf(ScriptConstant(signature.hex))
logger.debug("SigIndex: " + sigIndex)
//remove sig and it's corresponding BytesToPushOntoStack
val sigRemoved = txSignatureComponent.scriptPubKey.asm.slice(0,sigIndex-1) ++
txSignatureComponent.scriptPubKey.asm.slice(sigIndex+1,txSignatureComponent.scriptPubKey.asm.size)
logger.debug("Sig removed: " + sigRemoved)
val scriptPubKeySigRemoved = ScriptPubKey.fromAsm(sigRemoved)
TransactionSignatureComponentFactory.factory(txSignatureComponent,scriptPubKeySigRemoved)
} else txSignatureComponent
script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size)
} else script
logger.debug("Sig removed: " + sigRemoved)
val scriptPubKeySigRemoved = ScriptPubKey.fromAsm(sigRemoved)
TransactionSignatureComponentFactory.factory(txSignatureComponent,scriptPubKeySigRemoved)
}
val hashTypeByte = if (signature.bytes.size > 0) signature.bytes.last else 0x00.toByte
val hashType = HashTypeFactory.fromByte(hashTypeByte)
val hashForSignature = TransactionSignatureSerializer.hashForSignature(txSignatureComponentWithScriptPubKeyAdjusted.transaction,
txSignatureComponentWithScriptPubKeyAdjusted.inputIndex,txSignatureComponentWithScriptPubKeyAdjusted.scriptPubKey,hashType)
txSignatureComponentWithScriptPubKeyAdjusted.inputIndex,
txSignatureComponentWithScriptPubKeyAdjusted.scriptPubKey.asm, hashType)
logger.debug("Tx signature component: " + txSignatureComponentWithScriptPubKeyAdjusted)
logger.info("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature))
val isValid = pubKey.verify(hashForSignature,signature)
@ -86,13 +93,14 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
* if the signature does not match this public key, check it against the next
* public key in the sequence
* @param txSignatureComponent the tx signature component that contains all relevant transaction information
* @param script the script state this is needed in case there is an OP_CODESEPARATOR inside the script
* @param sigs the signatures that are being checked for validity
* @param pubKeys the public keys which are needed to verify that the signatures are correct
* @param flags the script verify flags which are rules to verify the signatures
* @return a boolean indicating if all of the signatures are valid against the given public keys
*/
@tailrec
final def multiSignatureEvaluator(txSignatureComponent : TransactionSignatureComponent,
final def multiSignatureEvaluator(txSignatureComponent : TransactionSignatureComponent, script : Seq[ScriptToken],
sigs : List[ECDigitalSignature], pubKeys : List[ECPublicKey], flags : Seq[ScriptFlag],
requiredSigs : Long) : TransactionSignatureCheckerResult = {
logger.info("Signatures inside of helper: " + sigs)
@ -113,12 +121,12 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
else if (!sigs.isEmpty && !pubKeys.isEmpty) {
val sig = sigs.head
val pubKey = pubKeys.head
val result = checkSignature(txSignatureComponent,pubKey,sig,flags)
val result = checkSignature(txSignatureComponent,script,pubKey,sig,flags)
result match {
case SignatureValidationSuccess =>
multiSignatureEvaluator(txSignatureComponent, sigs.tail,pubKeys.tail,flags, requiredSigs - 1)
multiSignatureEvaluator(txSignatureComponent, script, sigs.tail,pubKeys.tail,flags, requiredSigs - 1)
case SignatureValidationFailureIncorrectSignatures =>
multiSignatureEvaluator(txSignatureComponent, sigs,pubKeys.tail,flags, requiredSigs)
multiSignatureEvaluator(txSignatureComponent, script, sigs, pubKeys.tail,flags, requiredSigs)
case SignatureValidationFailureNotStrictDerEncoding =>
SignatureValidationFailureNotStrictDerEncoding
case SignatureValidationFailureSignatureCount =>

View file

@ -34,7 +34,9 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param script
* @return
*/
def serializeScriptCode(script : ScriptPubKey) : ScriptPubKey = removeOpCodeSeparators(script)
/*
def serializeScriptCode(script : ScriptPubKey) : ScriptPubKey = script
*/
/**
@ -46,9 +48,9 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param hashType
* @return
*/
def serializeForSignature(spendingTransaction : Transaction, inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = {
def serializeForSignature(spendingTransaction : Transaction, inputIndex : Int, script : Seq[ScriptToken], hashType : HashType) : Seq[Byte] = {
logger.debug("Serializing for signature")
logger.debug("ScriptPubKey: " + script)
logger.debug("Script: " + script)
// Clear input scripts in preparation for signing. If we're signing a fresh
// transaction that step isn't very helpful, but it doesn't add much cost relative to the actual
// EC math so we'll do it anyway.
@ -66,10 +68,10 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
// OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally, not actually
// ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required but if we don't
// do it, we could split off the main chain.
logger.info("Before Bitcoin-S Script to be connected: " + script.hex)
val scriptWithOpCodeSeparatorsRemoved : ScriptPubKey = removeOpCodeSeparators(script)
logger.info("Before Bitcoin-S Script to be connected: " + script)
val scriptWithOpCodeSeparatorsRemoved : Seq[ScriptToken] = script
logger.info("After Bitcoin-S Script to be connected: " + scriptWithOpCodeSeparatorsRemoved.hex)
logger.info("After Bitcoin-S Script to be connected: " + scriptWithOpCodeSeparatorsRemoved)
val inputToSign = inputSigsRemoved(inputIndex)
@ -111,7 +113,6 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
errorHash
} else {
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved,inputIndex)
logger.debug("Sighash single tx outputs: " + sigHashSingleTx.outputs)
sigHashSingleTx.bytes ++ sigHashBytes
}
@ -150,14 +151,13 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param hashType
* @return
*/
def hashForSignature(spendingTransaction : Transaction, inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = {
def hashForSignature(spendingTransaction : Transaction, inputIndex : Int, script : Seq[ScriptToken], hashType : HashType) : Seq[Byte] = {
//these first two checks are in accordance with behavior in bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1112-L1123
if (inputIndex >= spendingTransaction.inputs.size) {
logger.warn("Our inputIndex is out of the range of the inputs in the spending transaction")
errorHash
}
else if(hashType == SIGHASH_SINGLE && inputIndex >= spendingTransaction.outputs.size) {
} else if(hashType == SIGHASH_SINGLE && inputIndex >= spendingTransaction.outputs.size) {
logger.warn("When we have a SIGHASH_SINGLE we cannot have more inputs than outputs")
errorHash
} else {
@ -168,22 +168,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
}
}
/**
* Removes OP_CODESEPARATOR operations then returns the script
* format
* @return
*/
def removeOpCodeSeparators(script : ScriptPubKey) : ScriptPubKey = {
logger.info("Tokens: " + script.asm)
if (script.asm.contains(OP_CODESEPARATOR)) {
logger.debug("Contains OP_CODESEPARATOR")
//TODO: This needs to be tested
val scriptWithoutOpCodeSeparators : Seq[ScriptToken] = script.asm.filterNot(_ == OP_CODESEPARATOR)
val scriptBytes = BitcoinScriptUtil.asmToBytes(scriptWithoutOpCodeSeparators)
ScriptPubKey(scriptBytes)
} else script
}
/**
* Sets the input's sequence number to zero EXCEPT for the input at inputIndex

View file

@ -1,10 +1,10 @@
package org.bitcoins.protocol.transaction
import org.bitcoins.marshallers.transaction.{RawTransactionInputParser, TransactionElement}
import org.bitcoins.protocol.{CompactSizeUInt}
import org.bitcoins.protocol.script.{ScriptSignature, ScriptPubKey}
import org.bitcoins.util.{Factory, BitcoinSUtil}
import org.bitcoins.protocol.CompactSizeUInt
import org.bitcoins.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.script.constant.ScriptToken
import org.bitcoins.util.{BitcoinSUtil, Factory}
/**
* Created by chris on 12/26/15.
@ -80,10 +80,21 @@ object TransactionInput extends Factory[TransactionInput] {
def fromBytes(bytes : Seq[Byte]) : TransactionInput = RawTransactionInputParser.read(bytes).head
def apply(oldInput : TransactionInput, scriptSig : ScriptSignature) : TransactionInput = factory(oldInput,scriptSig)
def apply(oldInput : TransactionInput, script : Seq[ScriptToken]) : TransactionInput = {
val scriptSig = ScriptSignature.fromAsm(script)
apply(oldInput,scriptSig)
}
def apply(oldInput : TransactionInput, scriptPubKey: ScriptPubKey) : TransactionInput = factory(oldInput, scriptPubKey)
def apply(oldInput : TransactionInput,sequenceNumber : Long) : TransactionInput = factory(oldInput, sequenceNumber)
def apply(oldInput : TransactionInput,output : TransactionOutput, outputsTransaction : Transaction) : TransactionInput = factory(oldInput,output,outputsTransaction)
def apply(oldInput : TransactionInput, outPoint: TransactionOutPoint) : TransactionInput = factory(oldInput,outPoint)
def apply(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature, sequenceNumber : Long) : TransactionInput = factory(outPoint,scriptSignature,sequenceNumber)
def apply(bytes : Seq[Byte]) : TransactionInput = fromBytes(bytes)
}

View file

@ -92,10 +92,9 @@ sealed trait PreExecutionScriptProgram extends ScriptProgram
sealed trait ExecutionInProgressScriptProgram extends ScriptProgram {
/**
* The index of the last OP_CODESEPARATOR
*
* @return
*/
def lastCodeSeparator : Int
def lastCodeSeparator : Option[Int]
}
@ -140,7 +139,7 @@ object ScriptProgram {
*/
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
flags : Seq[ScriptFlag], lastCodeSeparator : Option[Int]) extends ExecutionInProgressScriptProgram
/**
* The implementation type for a script program that is finished being executed by the script interpreter
@ -168,7 +167,6 @@ object ScriptProgram {
/**
* 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
@ -187,7 +185,6 @@ object ScriptProgram {
/**
* Updates the program script verify flags
*
* @param oldProgram
* @param flags
* @return
@ -206,7 +203,6 @@ object ScriptProgram {
/**
* Changes the tokens in either the Stack or the Script depending in the indicator
*
* @param oldProgram
* @param tokens
* @param indicator
@ -234,7 +230,7 @@ object ScriptProgram {
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, tokens.toList, program.originalScript,
program.altStack, program.flags)
program.altStack, program.flags,program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the script for a program that has been fully executed")
}
@ -244,7 +240,7 @@ object ScriptProgram {
tokens.toList,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
tokens.toList, program.flags)
tokens.toList, program.flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the alt stack for a program that has been fully executed")
}
@ -255,7 +251,7 @@ object ScriptProgram {
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, tokens.toList,
program.altStack, program.flags)
program.altStack, program.flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the alt stack for a program that has been fully executed")
}
@ -265,7 +261,6 @@ object ScriptProgram {
/**
* Changes the stack tokens and script tokens in a ScriptProgram
*
* @param oldProgram
* @param stackTokens
* @param scriptTokens
@ -280,7 +275,6 @@ object ScriptProgram {
/**
* Updates the last OP_CODESEPARATOR index
*
* @param oldProgram
* @param lastCodeSeparator
* @return
@ -288,12 +282,11 @@ object ScriptProgram {
def factory(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
oldProgram.altStack, oldProgram.flags,lastCodeSeparator)
oldProgram.altStack, oldProgram.flags,Some(lastCodeSeparator))
}
/**
* Updates the tokens in either the stack or script and the last OP_CODESEPARATOR index
*
* @param oldProgram
* @param tokens
* @param indicator
@ -313,7 +306,6 @@ object ScriptProgram {
/**
* Updates the stack, script, alt stack of the given oldProgram
*
* @param oldProgram
* @param stack
* @param script
@ -331,7 +323,6 @@ object ScriptProgram {
* 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
* scriptSignature at the given input index
*
* @param transaction the transaction that is being checked
* @param scriptPubKey the scriptPubKey for which the input is spending
* @param inputIndex the input's index inside of transaction which we are spending
@ -366,7 +357,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
@ -385,7 +375,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 txSignatureComponent the relevant transaction information for execution of a script program
* @param stack the current stack state of the program
* @param script the script that we need to execute
@ -459,10 +448,10 @@ object ScriptProgram {
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)
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, None)
case None =>
ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,preExecutionScriptProgram.stack,preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, 0)
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, None)
}
}

View file

@ -99,16 +99,18 @@ trait ConstantInterpreter extends BitcoinSLogger {
//if we do not, mark the program as invalid
if (bytesNeeded == 0) ScriptProgram(program, ScriptNumber.zero :: program.stack, newScript)
else if (ScriptFlagUtil.requireMinimalData(program.flags) && bytesNeeded == 1 &&
constant.isInstanceOf[ScriptNumber] &&constant.toLong <= 16) {
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)
} else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) ScriptProgram(program,ScriptErrorBadOpCode)
} else if (bytesNeeded != 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)
} else ScriptProgram(program, constant :: program.stack, newScript)
} else ScriptProgram.apply(program, constant :: program.stack, newScript)
}

View file

@ -31,7 +31,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
/**
* The input is hashed using RIPEMD-160.
* @param program
*
* @param program
* @return
*/
def opRipeMd160(program : ScriptProgram) : ScriptProgram = {
@ -41,7 +42,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
/**
* The input is hashed using SHA-256.
* @param program
*
* @param program
* @return
*/
def opSha256(program : ScriptProgram) : ScriptProgram = {
@ -51,7 +53,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
/**
* The input is hashed two times with SHA-256.
* @param program
*
* @param program
* @return
*/
def opHash256(program : ScriptProgram) : ScriptProgram = {
@ -61,7 +64,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
/**
* The input is hashed using SHA-1.
* @param program
*
* @param program
* @return
*/
def opSha1(program : ScriptProgram) : ScriptProgram = {
@ -80,43 +84,55 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
*/
def opCheckSig(program : ScriptProgram) : ScriptProgram = {
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,ScriptErrorInvalidStackOperation)
} else {
val pubKey = ECFactory.publicKey(program.stack.head.bytes)
val signature = ECFactory.digitalSignature(program.stack.tail.head.bytes)
if (ScriptFlagUtil.requiresStrictDerEncoding(program.flags) && !DERSignatureUtil.isStrictDEREncoding(signature)) {
//this means all of the signatures must encoded according to BIP66 strict dersig
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
//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,ScriptErrorSigDer)
} else {
val restOfStack = program.stack.tail.tail
val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent,pubKey,
signature,program.flags)
logger.debug("signature verification isValid: " + result)
result match {
case SignatureValidationSuccess => ScriptProgram(program,
OP_TRUE :: restOfStack,program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
ScriptProgram(program,ScriptErrorSigHashType)
program match {
case preExecutionScriptProgram : PreExecutionScriptProgram =>
opCheckSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
case executedScriptprogram : ExecutedScriptProgram =>
executedScriptprogram
case executionInProgressScriptProgram : ExecutionInProgressScriptProgram =>
if (executionInProgressScriptProgram.stack.size < 2) {
logger.error("OP_CHECKSIG requires at lest two stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val pubKey = ECFactory.publicKey(executionInProgressScriptProgram.stack.head.bytes)
val signature = ECFactory.digitalSignature(executionInProgressScriptProgram.stack.tail.head.bytes)
if (ScriptFlagUtil.requiresStrictDerEncoding(executionInProgressScriptProgram.flags) &&
!DERSignatureUtil.isStrictDEREncoding(signature)) {
//this means all of the signatures must encoded according to BIP66 strict dersig
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
//script verification fails since the sig is not strictly der encoded
logger.error("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(executionInProgressScriptProgram,ScriptErrorSigDer)
} else {
val restOfStack = executionInProgressScriptProgram.stack.tail.tail
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature,program.flags)
logger.debug("signature verification isValid: " + result)
result match {
case SignatureValidationSuccess => ScriptProgram(program,
OP_TRUE :: restOfStack,program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
ScriptProgram(program,ScriptErrorSigHashType)
}
}
}
}
}
}
@ -152,15 +168,16 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
*/
def opCodeSeparator(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CODESEPARATOR, "Script top must be OP_CODESEPARATOR")
val indexOfOpCodeSeparator = program.originalScript.indexOf(OP_CODESEPARATOR)
require(indexOfOpCodeSeparator != -1,"The script we searched MUST contain an OP_CODESEPARTOR. Script: " + program.originalScript)
val e = program match {
case e : PreExecutionScriptProgram => ScriptProgram.toExecutionInProgress(e)
case e : ExecutionInProgressScriptProgram => ScriptProgram(e,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
case e : ExecutedScriptProgram => ScriptProgram(e,ScriptErrorUnknownError)
case e : PreExecutionScriptProgram =>
opCodeSeparator(ScriptProgram.toExecutionInProgress(e))
case e : ExecutionInProgressScriptProgram =>
val indexOfOpCodeSeparator = program.originalScript.size - program.script.size
ScriptProgram(e,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
case e : ExecutedScriptProgram =>
ScriptProgram(e,ScriptErrorUnknownError)
}
e
}
@ -174,109 +191,122 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
* signatures must be placed in the scriptSig using the same order as their corresponding public keys
* were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise.
* Due to a bug, one extra unused value is removed from the stack.
* @param program
*
* @param program
* @return
*/
def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
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,ScriptErrorSigNullDummy)
} else if (program.stack.size < 1) {
logger.error("OP_CHECKMULTISIG requires at least 1 stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : ScriptNumber = BitcoinScriptUtil.numPossibleSignaturesOnStack(program)
if (nPossibleSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of pubkeys in the script be negative")
ScriptProgram(program,ScriptErrorPubKeyCount)
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && !nPossibleSignatures.isShortestEncoding) {
logger.error("The required signatures and the possible signatures must be encoded as the shortest number possible")
ScriptProgram(program, ScriptErrorUnknownError)
} else if (program.stack.size < 2) {
logger.error("We need at least 2 operations on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
} else {
val mRequiredSignatures : ScriptNumber = BitcoinScriptUtil.numRequiredSignaturesOnStack(program)
if (ScriptFlagUtil.requireMinimalData(program.flags) && !mRequiredSignatures.isShortestEncoding) {
logger.error("The required signatures val must be the shortest encoding as possible")
return ScriptProgram(program,ScriptErrorUnknownError)
}
if (mRequiredSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of signatures specified in the script be negative")
return ScriptProgram(program,ScriptErrorSigCount)
}
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
val (pubKeysScriptTokens,stackWithoutPubKeys) =
(program.stack.tail.slice(0,nPossibleSignatures.num.toInt),
program.stack.tail.slice(nPossibleSignatures.num.toInt,program.stack.tail.size))
val pubKeys = pubKeysScriptTokens.map(key => ECFactory.publicKey(key.bytes))
logger.debug("Public keys on the stack: " + pubKeys)
logger.debug("mRequiredSignatures: " + mRequiredSignatures)
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens = program.stack.tail.slice(nPossibleSignatures.num.toInt + 1,
nPossibleSignatures.num.toInt + mRequiredSignatures.num.toInt + 1)
val signatures = signaturesScriptTokens.map(token => ECFactory.digitalSignature(token.bytes))
logger.debug("Signatures on the stack: " + signatures)
logger.debug("ScriptPubKey: " + program.txSignatureComponent.scriptPubKey)
//+1 is for bug in OP_CHECKMULTSIG that requires an extra OP to be pushed onto the stack
val stackWithoutPubKeysAndSignatures = stackWithoutPubKeys.tail.slice(mRequiredSignatures.num.toInt+1, stackWithoutPubKeys.tail.size)
val restOfStack = stackWithoutPubKeysAndSignatures
if (pubKeys.size > ScriptSettings.maxPublicKeysPerMultiSig) {
logger.error("We have more public keys than the maximum amount of public keys allowed")
ScriptProgram(program,ScriptErrorPubKeyCount)
} else if (signatures.size > pubKeys.size) {
logger.error("We have more signatures than public keys inside OP_CHECKMULTISIG")
ScriptProgram(program, ScriptErrorSigCount)
program match {
case preExecutionScriptProgram : PreExecutionScriptProgram =>
opCheckMultiSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
case executedScriptProgram : ExecutedScriptProgram =>
executedScriptProgram
case executionInProgressScriptProgram : ExecutionInProgressScriptProgram =>
if (executionInProgressScriptProgram.flags.contains(ScriptVerifyNullDummy) &&
executionInProgressScriptProgram.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(executionInProgressScriptProgram,ScriptErrorSigNullDummy)
} else if (program.stack.size < 1) {
logger.error("OP_CHECKMULTISIG requires at least 1 stack elements")
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
} else {
val isValidSignatures : TransactionSignatureCheckerResult =
TransactionSignatureChecker.multiSignatureEvaluator(program.txSignatureComponent,signatures,
pubKeys,program.flags,mRequiredSignatures.num)
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : ScriptNumber = BitcoinScriptUtil.numPossibleSignaturesOnStack(program)
if (nPossibleSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of pubkeys in the script be negative")
ScriptProgram(program,ScriptErrorPubKeyCount)
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && !nPossibleSignatures.isShortestEncoding) {
logger.error("The required signatures and the possible signatures must be encoded as the shortest number possible")
ScriptProgram(executionInProgressScriptProgram, ScriptErrorUnknownError)
} else if (program.stack.size < 2) {
logger.error("We need at least 2 operations on the stack")
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
} else {
val mRequiredSignatures: ScriptNumber = BitcoinScriptUtil.numRequiredSignaturesOnStack(program)
isValidSignatures match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
//that all of the signatures were valid signatures for the given
//public keys
ScriptProgram(program, OP_TRUE :: restOfStack, program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
//this means the script fails immediately
//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)
case SignatureValidationFailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(program, OP_FALSE :: restOfStack, program.script.tail)
case SignatureValidationFailureSignatureCount =>
//means that we did not have enough signatures for OP_CHECKMULTISIG
ScriptProgram(program, ScriptErrorInvalidStackOperation)
case SignatureValidationFailurePubKeyEncoding =>
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
ScriptProgram(program,ScriptErrorSigHashType)
if (ScriptFlagUtil.requireMinimalData(program.flags) && !mRequiredSignatures.isShortestEncoding) {
logger.error("The required signatures val must be the shortest encoding as possible")
return ScriptProgram(executionInProgressScriptProgram, ScriptErrorUnknownError)
}
if (mRequiredSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of signatures specified in the script be negative")
return ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigCount)
}
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
val (pubKeysScriptTokens, stackWithoutPubKeys) =
(program.stack.tail.slice(0, nPossibleSignatures.num.toInt),
program.stack.tail.slice(nPossibleSignatures.num.toInt, program.stack.tail.size))
val pubKeys = pubKeysScriptTokens.map(key => ECFactory.publicKey(key.bytes))
logger.debug("Public keys on the stack: " + pubKeys)
logger.debug("mRequiredSignatures: " + mRequiredSignatures)
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens = program.stack.tail.slice(nPossibleSignatures.num.toInt + 1,
nPossibleSignatures.num.toInt + mRequiredSignatures.num.toInt + 1)
val signatures = signaturesScriptTokens.map(token => ECFactory.digitalSignature(token.bytes))
logger.debug("Signatures on the stack: " + signatures)
logger.debug("ScriptPubKey: " + program.txSignatureComponent.scriptPubKey)
//+1 is for bug in OP_CHECKMULTSIG that requires an extra OP to be pushed onto the stack
val stackWithoutPubKeysAndSignatures = stackWithoutPubKeys.tail.slice(mRequiredSignatures.num.toInt + 1, stackWithoutPubKeys.tail.size)
val restOfStack = stackWithoutPubKeysAndSignatures
if (pubKeys.size > ScriptSettings.maxPublicKeysPerMultiSig) {
logger.error("We have more public keys than the maximum amount of public keys allowed")
ScriptProgram(executionInProgressScriptProgram, ScriptErrorPubKeyCount)
} else if (signatures.size > pubKeys.size) {
logger.error("We have more signatures than public keys inside OP_CHECKMULTISIG")
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigCount)
} else {
//remove the last OP_CODESEPARATOR
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
val isValidSignatures: TransactionSignatureCheckerResult =
TransactionSignatureChecker.multiSignatureEvaluator(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, signatures,
pubKeys, program.flags, mRequiredSignatures.num)
isValidSignatures match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
//that all of the signatures were valid signatures for the given
//public keys
ScriptProgram(executionInProgressScriptProgram, OP_TRUE :: restOfStack, program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
//this means the script fails immediately
//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(executionInProgressScriptProgram, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(executionInProgressScriptProgram, OP_FALSE :: restOfStack, program.script.tail)
case SignatureValidationFailureSignatureCount =>
//means that we did not have enough signatures for OP_CHECKMULTISIG
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
case SignatureValidationFailurePubKeyEncoding =>
//means that a public key was not encoded correctly
ScriptProgram(executionInProgressScriptProgram, ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
ScriptProgram(program, ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigHashType)
}
}
}
}
}
}
}
/**
* Runs OP_CHECKMULTISIG with an OP_VERIFY afterwards
* @param program
*
* @param program
* @return
*/
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
@ -317,4 +347,17 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
}
}
/**
* Removes the OP_CODESEPARATOR in the original script according to
* the last code separator index in the script
* @param program
* @return
*/
def removeOpCodeSeparator(program : ExecutionInProgressScriptProgram) : Seq[ScriptToken] = {
if (program.lastCodeSeparator.isDefined) {
program.originalScript.slice(program.lastCodeSeparator.get+1, program.originalScript.size)
} else program.originalScript
}
}

View file

@ -168,7 +168,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case OP_0 :: t => loop(ScriptProgram(p, ScriptNumber.zero :: p.stack, t))
case (scriptNumberOp : ScriptNumberOperation) :: t =>
loop(ScriptProgram(p, ScriptNumber(scriptNumberOp.num) :: p.stack, t))
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t => loop(pushScriptNumberBytesToStack(p))
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))

View file

@ -26,27 +26,13 @@ import scala.collection.JavaConversions._
class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val scriptPubKey = BitcoinjConversions.toScriptPubKey(BitcoinJTestUtil.multiSigScript)
"TransactionSignatureSerializer" must "serialize a given script signature without OP_CODESEPARATORS" in {
val scriptPubKey = TestUtil.scriptPubKey
val expectedScript = TransactionSignatureSerializer.removeOpCodeSeparators(scriptPubKey)
TransactionSignatureSerializer.serializeScriptCode(scriptPubKey) must be (expectedScript)
}
it must "not remove any bytes from a script that does not contain OP_CODESEPARATORS" in {
//from b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
val scriptHex = TestUtil.rawP2PKHScriptPubKey
val scriptPubKey = ScriptPubKey(scriptHex)
val hexAfterRemovingOpCodeSeparators = TransactionSignatureSerializer.removeOpCodeSeparators(scriptPubKey).hex
//for some reason p2pkh scripts do not include the amount of bytes included on the script aka the lead byte
hexAfterRemovingOpCodeSeparators must be (scriptHex)
}
it must "serialize a transaction for SIGHASH_ALL correctly" in {
"TransactionSignatureSerializer" must "serialize a transaction for SIGHASH_ALL correctly" in {
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val sigBytes : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey,SIGHASH_ALL())
val sigBytes : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_ALL())
val bitcoinjSerialization = BitcoinSUtil.encodeHex(
BitcoinJSignatureSerialization.serializeForSignature(BitcoinJTestUtil.multiSigTransaction,0,
BitcoinJTestUtil.multiSigScript.getProgram(),SIGHASH_ALL().byte)
@ -60,7 +46,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val bitcoinsTxSigHash : Seq[Byte] = TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey,SIGHASH_ALL())
val bitcoinsTxSigHash : Seq[Byte] = TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_ALL())
val bitcoinjTxSigHash = BitcoinSUtil.encodeHex(
BitcoinJSignatureSerialization.hashForSignature(BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram(),SIGHASH_ALL().byte)
)
@ -72,7 +58,8 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serialiazedTxForSig : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey,SIGHASH_SINGLE)
val serialiazedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_SINGLE)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE.byte))
@ -84,7 +71,8 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serialiazedTxForSig : Seq[Byte] = TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey,SIGHASH_SINGLE)
val serialiazedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_SINGLE)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE.byte))
@ -96,7 +84,8 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serialiazedTxForSig : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey,SIGHASH_NONE)
val serialiazedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_NONE)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE.byte))
@ -108,7 +97,8 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serialiazedTxForSig : Seq[Byte] = TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey,SIGHASH_NONE)
val serialiazedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm,SIGHASH_NONE)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE.byte))
@ -121,7 +111,8 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey, SIGHASH_ANYONECANPAY)
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_ANYONECANPAY)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ANYONECANPAY.byte))
@ -135,7 +126,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey, SIGHASH_ANYONECANPAY)
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_ANYONECANPAY)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ANYONECANPAY.byte))
@ -148,7 +139,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey, SIGHASH_ALL_ANYONECANPAY)
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_ALL_ANYONECANPAY)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ALL_ANYONECANPAY.byte))
@ -161,7 +152,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey, SIGHASH_SINGLE_ANYONECANPAY)
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_SINGLE_ANYONECANPAY)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE_ANYONECANPAY.byte))
@ -174,7 +165,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey, SIGHASH_NONE_ANYONECANPAY)
TransactionSignatureSerializer.serializeForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_NONE_ANYONECANPAY)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE_ANYONECANPAY.byte))
@ -187,7 +178,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxHashForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey, SIGHASH_ALL_ANYONECANPAY)
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_ALL_ANYONECANPAY)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ALL_ANYONECANPAY.byte))
@ -200,7 +191,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxHashForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey, SIGHASH_SINGLE_ANYONECANPAY)
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_SINGLE_ANYONECANPAY)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE_ANYONECANPAY.byte))
@ -213,7 +204,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxHashForSig : Seq[Byte] =
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey, SIGHASH_NONE_ANYONECANPAY)
TransactionSignatureSerializer.hashForSignature(spendingTx,0,scriptPubKey.asm, SIGHASH_NONE_ANYONECANPAY)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE_ANYONECANPAY.byte))
@ -246,7 +237,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val hashType = spendingInput.scriptSignature.hashType(spendingInput.scriptSignature.signatures.head)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,hashType
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm,hashType
))
serializedTxForSig must be (BitcoinSUtil.encodeHex(bitcoinjSerializeForSig))
}
@ -272,7 +263,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val hashType = spendingInput.scriptSignature.hashType(spendingInput.scriptSignature.signatures.head)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,hashType
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm,hashType
))
@ -294,7 +285,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
bitcoinjTx, inputIndex, creditingOutput.scriptPubKey.bytes.toArray, hashType.byte
)
val hashedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,hashType
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm,hashType
))
hashedTxForSig must be (BitcoinSUtil.encodeHex(bitcoinjHashForSig))
}
@ -315,7 +306,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
bitcoinjTx, inputIndex, creditingOutput.scriptPubKey.bytes.toArray, hashType.byte
)
val hashedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,hashType
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm, hashType
))
hashedTxForSig must be (BitcoinSUtil.encodeHex(bitcoinjHashForSig))
}
@ -323,14 +314,6 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
}
it must "remove OP_CODESEPARATORs from a scriptPubKey" in {
val scriptPubKeyWithOpCodeSeparators = ScriptPubKey.fromAsm(
Seq(OP_0,OP_1,OP_2,OP_CODESEPARATOR, OP_3,OP_CODESEPARATOR, OP_4, OP_CODESEPARATOR))
val expectedScriptPubKey = ScriptPubKey.fromAsm(Seq(OP_0,OP_1,OP_2, OP_3, OP_4))
val actualScriptPubKey = TransactionSignatureSerializer.removeOpCodeSeparators(scriptPubKeyWithOpCodeSeparators)
actualScriptPubKey must be (expectedScriptPubKey)
}
it must "hash a transaction that has script operations after OP_CHECKSIGVERIFY" in {
//this example is from tx_valid.json
@ -350,7 +333,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
)
val hashedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_ALL(0x01)
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL(0x01)
))
hashedTxForSig must be (BitcoinSUtil.encodeHex(bitcoinjHashForSig))
@ -368,7 +351,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_ALL(0x01)
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm, SIGHASH_ALL(0x01)
))
//serialization is from bitcoin core
@ -390,7 +373,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
bitcoinjTx, inputIndex, scriptPubKey.bytes.toArray, SIGHASH_ALL_ANYONECANPAY.byte
)
val hashedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_ALL_ANYONECANPAY
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL_ANYONECANPAY
))
//hash is from bitcoin core
hashedTxForSig must be ("57f5a54d548db73fa8ef7a43d011120f9935fe792f0a0630d28ee70b4c72a7e8")
@ -407,7 +390,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_SINGLE))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_SINGLE))
//serialization is from bitcoin core
serializedTxForSig must be ("010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000001976a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88acffffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e8040100000000000000003f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee0100000000000000000180841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac0000000003000000")
@ -424,7 +407,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_SINGLE))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_SINGLE))
//serialization is from bitcoin core
serializedTxForSig must be ("010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf630000000000000000007d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e8040100000000000000003f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000001976a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88acffffffff03ffffffffffffffff00ffffffffffffffff00e0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac0000000003000000")
@ -441,10 +424,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
require(scriptPubKey.hex == "4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288ac")
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey,SIGHASH_ALL(1.toByte)))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL(1.toByte)))
//serialization is from bitcoin core
println("Serialized tx for sig: " + serializedTxForSig)
serializedTxForSig must be ("0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c500000000ca4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288acffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac0000000001000000")
}
}

View file

@ -52,8 +52,8 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
val lines =
"""
|[
|[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]],
|"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "P2SH"]
|[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
|"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH"]
|
|]
""".stripMargin

View file

@ -26,7 +26,7 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
it must "update the OP_CODESEPARATOR index" in {
val index = 999
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,index)
program.lastCodeSeparator must be (999)
program.lastCodeSeparator must be (Some(index))
}
it must "update the OP_CODESEPARATOR index & stack simultaneously" in {
@ -34,7 +34,7 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
val stack = Seq(OP_0)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,ScriptProgram.Stack,index)
program.stack must be (stack)
program.lastCodeSeparator must be (index)
program.lastCodeSeparator must be (Some(index))
}
it must "update the OP_CODESEPARATOR index & altStack simultaneously" in {
@ -42,7 +42,7 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
val altStack = Seq(OP_0)
val program = ScriptProgram(TestUtil.testProgramExecutionInProgress,altStack,ScriptProgram.AltStack,index)
program.altStack must be (altStack)
program.lastCodeSeparator must be (index)
program.lastCodeSeparator must be (Some(index))
}

View file

@ -1,5 +1,6 @@
package org.bitcoins.script.crypto
import org.bitcoins.crypto.TransactionSignatureSerializer
import org.bitcoins.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.protocol.transaction._
import org.bitcoins.script.result._
@ -133,18 +134,6 @@ class CryptoInterpreterTest extends FlatSpec with MustMatchers with CryptoInterp
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
}
it must "evaluate an OP_CHECKSIG for a p2pk transaction" in {
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(TestUtil.p2pkScriptPubKey)
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,TestUtil.p2pkScriptSig,outputIndex)
val baseProgram = ScriptProgram(spendingTx,creditingTx.outputs(0).scriptPubKey,0,ScriptFlagFactory.empty)
val stack = Seq(TestUtil.p2pkScriptPubKey.asm(1)) ++ TestUtil.p2pkScriptSig.asm.tail
val script = List(TestUtil.p2pkScriptPubKey.asm.last)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckSig(program)
newProgram.stack must be (List(OP_TRUE))
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_CHECKMULTISIG for a p2sh transaction" in {
@ -213,13 +202,8 @@ class CryptoInterpreterTest extends FlatSpec with MustMatchers with CryptoInterp
val stack = List()
val script = Seq(OP_CODESEPARATOR)
val program = ScriptProgram(ScriptProgram(TestUtil.testProgramExecutionInProgress,stack,script),script,ScriptProgram.OriginalScript)
println(program)
println(program.script)
println(program.originalScript)
val newProgram = ScriptProgramTestUtil.toExecutionInProgressScriptProgram(opCodeSeparator(program))
newProgram.lastCodeSeparator must be (0)
newProgram.lastCodeSeparator must be (Some(0))
}
}