Implementing a generator to created a signed witness transaction taht spends a P2WPKH output, create a spec that verifies we can spend all of those outputs

This commit is contained in:
Chris Stewart 2016-12-08 17:12:21 -06:00
parent 86d8a92657
commit a106d23610
10 changed files with 181 additions and 200 deletions

View file

@ -55,9 +55,7 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
logger.error("The public key given for signature checking was not encoded correctly, err: " + result)
result
} else {
val sigsRemovedScript = calculateScriptForSigning(txSignatureComponent,signature,script)
val sigsRemovedScript = BitcoinScriptUtil.calculateScriptForChecking(txSignatureComponent,signature,script)
val hashTypeByte = if (signature.bytes.nonEmpty) signature.bytes.last else 0x00.toByte
val hashType = HashType(Seq(0.toByte, 0.toByte, 0.toByte, hashTypeByte))
@ -126,107 +124,6 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
SignatureValidationSuccess
} else SignatureValidationFailureIncorrectSignatures
}
/**
* Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists
*
* @return
*/
def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = {
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 = script.indexOf(ScriptConstant(signature.hex))
logger.debug("SigIndex: " + sigIndex)
//remove sig and it's corresponding BytesToPushOntoStack
script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size)
} else script
}
/** Removes the list of [[ECDigitalSignature]] from the list of [[ScriptToken]] */
def removeSignaturesFromScript(sigs : Seq[ECDigitalSignature], script : Seq[ScriptToken]) : Seq[ScriptToken] = {
@tailrec
def loop(remainingSigs : Seq[ECDigitalSignature], scriptTokens : Seq[ScriptToken]) : Seq[ScriptToken] = {
remainingSigs match {
case Nil => scriptTokens
case h :: t =>
val newScriptTokens = removeSignatureFromScript(h,scriptTokens)
loop(t,newScriptTokens)
}
}
loop(sigs,script)
}
/** Prepares the script we spending to be serialized for our transaction signature serialization algorithm
* We need to check if the scriptSignature has a redeemScript
* In that case, we need to pass the redeemScript to the TransactionSignatureChecker
*
* In the case we have a P2SH(P2WSH) we need to pass the witness's redeem script to the [[TransactionSignatureChecker]]
* instead of passing the [[WitnessScriptPubKey]] inside of the [[P2SHScriptSignature]]'s redeem script.
* */
def calculateScriptForSigning(txSignatureComponent: TransactionSignatureComponent, signature: ECDigitalSignature,
script: Seq[ScriptToken]): Seq[ScriptToken] = txSignatureComponent match {
case base: BaseTransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
//needs to be here for removing all sigs from OP_CHECKMULTISIG
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L177
//Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures.
//In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually
// present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01
// We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also
// using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged'
//signature would still be in the hashed script, and the normal signature would fail."
logger.info("Replacing redeemScript in txSignature component")
logger.info("Redeem script: " + s.redeemScript)
val sigsRemoved = removeSignaturesFromScript(s.signatures,s.redeemScript.asm)
sigsRemoved
case x @ (_ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature | EmptyScriptSignature) =>
logger.debug("Script before sigRemoved: " + script)
logger.debug("Signature: " + signature)
val sigsRemoved = removeSignatureFromScript(signature,script)
sigsRemoved
}
case wtxSigComponent : WitnessV0TransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
//this is for the case of P2SH(P2WSH), we need to sign the redeem script inside of the
//witness, NOT the witnessScriptPubKey redeemScript inside of the P2SHScriptSignature
logger.info("Redeem script: " + s.redeemScript)
s.redeemScript match {
case w : WitnessScriptPubKey =>
if (w.bytes.size == 23) {
//P2SH(P2WPKH)
val sigsRemoved = removeSignaturesFromScript(s.signatures,wtxSigComponent.scriptPubKey.asm)
sigsRemoved
} else {
//P2SH(P2WSH)
//if the redeem script is a witenss script pubkey, the true redeem script is the first item inside the witness
val redeemScriptBytes = wtxSigComponent.witness.stack.head
val compact = CompactSizeUInt.calculateCompactSizeUInt(redeemScriptBytes)
val witnessRedeemScript = ScriptPubKey(compact.bytes ++ redeemScriptBytes)
val sigsRemoved = removeSignaturesFromScript(s.signatures,witnessRedeemScript.asm)
sigsRemoved
}
case x @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : NonStandardScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | EmptyScriptPubKey) =>
val sigsRemoved = removeSignaturesFromScript(s.signatures, x.asm)
sigsRemoved
}
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature | EmptyScriptSignature =>
logger.debug("Script before sigRemoved: " + script)
logger.debug("Signature: " + signature)
val sigsRemoved = removeSignatureFromScript(signature,script)
sigsRemoved
}
}
}
object TransactionSignatureChecker extends TransactionSignatureChecker

View file

@ -9,7 +9,7 @@ import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.serializers.RawBitcoinSerializerHelper
import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, CryptoUtil}
/**
* Created by chris on 2/16/16.
@ -192,6 +192,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
logger.debug("Script: " + script)
val scriptBytes = script.flatMap(_.bytes)
val fe: Seq[Byte] => Seq[Byte] = { bytes: Seq[Byte] => BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(bytes)) }
val serializationForSig: Seq[Byte] = fe(spendingTx.version.bytes) ++ outPointHash.getOrElse(Nil) ++ sequenceHash.getOrElse(Nil) ++
spendingTx.inputs(inputIndexInt).previousOutput.bytes ++ CompactSizeUInt.calculateCompactSizeUInt(scriptBytes).bytes ++
scriptBytes ++ fe(amount.bytes) ++ fe(spendingTx.inputs(inputIndexInt).sequence.bytes) ++
@ -212,20 +213,18 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
}
/**
* Wrapper function for hashForSignature
* @param txSignatureComponent this contains the transaction and inputIndex for hashForSignature
* @param txSigComponent this contains the transaction and inputIndex for hashForSignature
* @param hashType
* @return
*/
def hashForSignature(txSignatureComponent: TransactionSignatureComponent, hashType: HashType): DoubleSha256Digest = txSignatureComponent match {
case b: BaseTransactionSignatureComponent =>
val hash = hashForSignature(b.transaction,b.inputIndex,
b.scriptPubKey.asm,hashType)
logger.info("btx signature hash: " + hash)
hash
case w : WitnessV0TransactionSignatureComponent =>
val hash = hashForSignature(w.transaction,w.inputIndex,w.scriptPubKey.asm, hashType, w.amount)
logger.info("wtx signature hash: " + hash)
hash
def hashForSignature(txSigComponent: TransactionSignatureComponent, hashType: HashType): DoubleSha256Digest = {
val script = BitcoinScriptUtil.calculateScriptForSigning(txSigComponent,txSigComponent.scriptPubKey.asm)
txSigComponent match {
case t : BaseTransactionSignatureComponent =>
hashForSignature(t.transaction,t.inputIndex,script,hashType)
case t : WitnessV0TransactionSignatureComponent =>
hashForSignature(t.transaction,t.inputIndex, script, hashType,t.amount)
}
}
@ -236,13 +235,11 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param inputIndex
* @return
*/
private def setSequenceNumbersZero(inputs : Seq[TransactionInput], inputIndex : UInt32) : Seq[TransactionInput] = {
for {
(input,index) <- inputs.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) input
else TransactionInput(input,UInt32.zero)
}
private def setSequenceNumbersZero(inputs : Seq[TransactionInput], inputIndex : UInt32) : Seq[TransactionInput] = for {
(input,index) <- inputs.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) input
else TransactionInput(input,UInt32.zero)
}
/**

View file

@ -19,6 +19,7 @@ trait Policy {
* details.
*/
def mandatoryScriptVerifyFlags : Seq[ScriptFlag] = Seq(ScriptVerifyP2SH)
/**
* The default script verify flags used to validate the blockchain
* and bitcoin transactions
@ -27,7 +28,8 @@ trait Policy {
def standardScriptVerifyFlags : Seq[ScriptFlag] = mandatoryScriptVerifyFlags ++ Seq(ScriptVerifyDerSig, ScriptVerifyStrictEnc,
ScriptVerifyMinimalData, ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs,
ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify,
ScriptVerifyLowS)
ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail,
ScriptVerifyNullDummy, ScriptVerifyWitnessPubKeyType)
}

View file

@ -11,7 +11,7 @@ import org.bitcoins.core.util.{BitcoinSUtil, Factory}
*/
sealed trait ScriptWitness {
/** The [[ScriptToken]]s that are placed on to the stack when evaluating a witness program */
/** The byte vectors that are placed on to the stack when evaluating a witness program */
def stack : Seq[Seq[Byte]]
override def toString = stack.map(BitcoinSUtil.encodeHex(_)).toString

View file

@ -84,7 +84,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
} else {
val restOfStack = executionInProgressScriptProgram.stack.tail.tail
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, flags)
@ -249,7 +249,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullDummy)
} else {
//remove the last OP_CODESEPARATOR
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
val isValidSignatures: TransactionSignatureCheckerResult =
TransactionSignatureChecker.multiSignatureEvaluator(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, signatures,
@ -338,17 +338,4 @@ 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

@ -1,13 +1,14 @@
package org.bitcoins.core.util
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.crypto._
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script.{CLTVScriptPubKey, CSVScriptPubKey, EmptyScriptPubKey, _}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_CHECKSIGVERIFY}
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.result.{ScriptError, ScriptErrorPubKeyType, ScriptErrorWitnessPubKeyType}
import org.bitcoins.core.script.{ScriptOperation, ScriptProgram, ScriptSettings}
import org.bitcoins.core.script.{ExecutionInProgressScriptProgram, ScriptOperation, ScriptProgram, ScriptSettings}
import scala.annotation.tailrec
import scala.util.Try
@ -15,32 +16,21 @@ import scala.util.Try
/**
* Created by chris on 3/2/16.
*/
trait BitcoinScriptUtil {
trait BitcoinScriptUtil extends BitcoinSLogger {
/**
* Takes in a sequence of script tokens and converts them to their hexadecimal value
* @param asm
* @return
*/
/** Takes in a sequence of script tokens and converts them to their hexadecimal value */
def asmToHex(asm : Seq[ScriptToken]) : String = {
val hex = asm.map(_.hex).mkString
hex
}
/**
* Converts a sequence of script tokens to them to their byte values
* @param asm
* @return
*/
/** Converts a sequence of script tokens to them to their byte values */
def asmToBytes(asm : Seq[ScriptToken]) : Seq[Byte] = BitcoinSUtil.decodeHex(asmToHex(asm))
/**
* Filters out push operations in our sequence of script tokens
* this removes OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 and all ByteToPushOntoStack tokens
* @param asm
* @return
*/
* this removes OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 and all ByteToPushOntoStack tokens */
def filterPushOps(asm : Seq[ScriptToken]) : Seq[ScriptToken] = {
asm.filterNot(op => op.isInstanceOf[BytesToPushOntoStack]
|| op == OP_PUSHDATA1
@ -51,10 +41,7 @@ trait BitcoinScriptUtil {
/**
* Returns true if the given script token counts towards our max script operations in a script
* See https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L269-L271
* which is how bitcoin core handles this
* @param token
* @return
*/
* which is how bitcoin core handles this */
def countsTowardsScriptOpLimit(token : ScriptToken) : Boolean = token match {
case scriptOp : ScriptOperation if (scriptOp.opCode > OP_16.opCode) => true
case _ : ScriptToken => false
@ -91,8 +78,6 @@ trait BitcoinScriptUtil {
* This can only be called when an OP_CHECKMULTISIG operation is about to be executed
* on the stack
* For instance if this was a 2/3 multisignature script, it would return the number 3
* @param program
* @return
*/
def numPossibleSignaturesOnStack(program : ScriptProgram) : ScriptNumber = {
require(program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(OP_CHECKMULTISIGVERIFY),
@ -108,8 +93,6 @@ trait BitcoinScriptUtil {
/**
* Returns the number of required signatures on the stack, for instance if this was a
* 2/3 multisignature script, it would return the number 2
* @param program
* @return
*/
def numRequiredSignaturesOnStack(program : ScriptProgram) : ScriptNumber = {
require(program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(OP_CHECKMULTISIGVERIFY),
@ -129,8 +112,6 @@ trait BitcoinScriptUtil {
* Determines if a script contains only script operations
* This is equivalent to
* https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L213
* @param script
* @return
*/
def isPushOnly(script : Seq[ScriptToken]) : Boolean = {
@tailrec
@ -176,12 +157,7 @@ trait BitcoinScriptUtil {
}
}
/**
* Calculates the push operation for the given [[ScriptToken]]
*
* @param scriptToken
* @return
*/
/** Calculates the push operation for the given [[ScriptToken]] */
def calculatePushOp(scriptToken : ScriptToken) : Seq[ScriptToken] = {
//push ops following an OP_PUSHDATA operation are interpreted as unsigned numbers
val scriptTokenSize = UInt32(scriptToken.bytes.size)
@ -211,20 +187,13 @@ trait BitcoinScriptUtil {
/**
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param constant
* @return
*/
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237 */
def isShortestEncoding(constant : ScriptConstant) : Boolean = isShortestEncoding(constant.bytes)
/**
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param bytes
* @return
*/
def isShortestEncoding(bytes : Seq[Byte]) : Boolean = {
// If the most-significant-byte - excluding the sign bit - is zero
@ -246,16 +215,12 @@ trait BitcoinScriptUtil {
* Whenever a script constant is interpreted to a number BIP62 should enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param hex
* @return
*/
def isShortestEncoding(hex : String) : Boolean = isShortestEncoding(BitcoinSUtil.decodeHex(hex))
/**
* Checks the public key encoding according to bitcoin core's function
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
*
* @param key the key whose encoding we are checking
* @param key the key whose encoding we are checking
* @param program the program whose flags which dictate the rules for the public keys encoding
* @return if the key is encoded correctly against the rules give in the flags parameter
*/
@ -264,8 +229,7 @@ trait BitcoinScriptUtil {
/**
* Checks the public key encoding according to bitcoin core's function
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
*
* @param key the key whose encoding we are checking
* @param key the key whose encoding we are checking
* @param flags the flags which dictate the rules for the public keys encoding
* @return if the key is encoded correctly against the rules givein the flags parameter
*/
@ -278,8 +242,7 @@ trait BitcoinScriptUtil {
/**
* Returns true if the key is compressed or uncompressed, false otherwise
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L66
*
* @param key the public key that is being checked
* @param key the public key that is being checked
* @return true if the key is compressed/uncompressed otherwise false
*/
def isCompressedOrUncompressedPubKey(key : ECPublicKey) : Boolean = {
@ -326,6 +289,133 @@ trait BitcoinScriptUtil {
Some(ScriptErrorWitnessPubKeyType)
} else None
}
/** Prepares the script we spending to be serialized for our transaction signature serialization algorithm
* We need to check if the scriptSignature has a redeemScript
* In that case, we need to pass the redeemScript to the TransactionSignatureChecker
*
* In the case we have a P2SH(P2WSH) we need to pass the witness's redeem script to the [[TransactionSignatureChecker]]
* instead of passing the [[WitnessScriptPubKey]] inside of the [[P2SHScriptSignature]]'s redeem script.
* */
def calculateScriptForChecking(txSignatureComponent: TransactionSignatureComponent,
signature: ECDigitalSignature,script: Seq[ScriptToken]): Seq[ScriptToken] = {
val scriptWithSigRemoved = calculateScriptForSigning(txSignatureComponent, script)
removeSignatureFromScript(signature,scriptWithSigRemoved)
}
def calculateScriptForSigning(txSignatureComponent: TransactionSignatureComponent, script: Seq[ScriptToken]): Seq[ScriptToken] = txSignatureComponent match {
case base: BaseTransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
//needs to be here for removing all sigs from OP_CHECKMULTISIG
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L177
//Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures.
//In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually
// present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01
// We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also
// using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged'
//signature would still be in the hashed script, and the normal signature would fail."
logger.info("Replacing redeemScript in txSignature component")
logger.info("Redeem script: " + s.redeemScript)
val sigsRemoved = removeSignaturesFromScript(s.signatures,s.redeemScript.asm)
sigsRemoved
case x @ (_ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature | EmptyScriptSignature) =>
script
}
case wtxSigComponent : WitnessV0TransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
// this is for the case of P2SH(P2WSH) or P2SH(P2WPKH)
// in the case of P2SH(P2WPKH) we need to sign the P2WPKH asm
// in the case of P2SH(P2WSH) we need to sign the redeem script inside of the
// witness, NOT the witnessScriptPubKey redeemScript inside of the P2SHScriptSignature
logger.debug("Redeem script: " + s.redeemScript)
s.redeemScript match {
case w : WitnessScriptPubKey =>
//rebuild scriptPubKey asm
val scriptEither: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] = w.witnessVersion.rebuild(wtxSigComponent.witness,w.witnessProgram)
parseScriptEither(scriptEither)
case x @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : NonStandardScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | EmptyScriptPubKey) =>
val sigsRemoved = removeSignaturesFromScript(s.signatures, x.asm)
sigsRemoved
}
case EmptyScriptSignature =>
logger.info("wtxSigComponent.scriptPubKey: " + wtxSigComponent.scriptPubKey)
wtxSigComponent.scriptPubKey match {
case w : WitnessScriptPubKeyV0 =>
//for bare P2WPKH
logger.info("wtxSigComponent.witness: " + wtxSigComponent.witness)
logger.info("w.witnessProgram: " + w.witnessProgram)
val scriptEither = w.witnessVersion.rebuild(wtxSigComponent.witness,w.witnessProgram)
logger.info("scriptEither: " + scriptEither)
val s = parseScriptEither(scriptEither)
logger.info("P2WPKH: " + s)
s
case _ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : NonStandardScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey |
_: UnassignedWitnessScriptPubKey | EmptyScriptPubKey =>
logger.info("Empty script signature, returning original script: " + script)
script
}
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature =>
script
}
}
/**
* Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists
*/
def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = {
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 = script.indexOf(ScriptConstant(signature.hex))
logger.debug("SigIndex: " + sigIndex)
//remove sig and it's corresponding BytesToPushOntoStack
script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size)
} else script
}
/** Removes the list of [[ECDigitalSignature]] from the list of [[ScriptToken]] */
def removeSignaturesFromScript(sigs : Seq[ECDigitalSignature], script : Seq[ScriptToken]) : Seq[ScriptToken] = {
@tailrec
def loop(remainingSigs : Seq[ECDigitalSignature], scriptTokens : Seq[ScriptToken]) : Seq[ScriptToken] = {
remainingSigs match {
case Nil => scriptTokens
case h :: t =>
val newScriptTokens = removeSignatureFromScript(h,scriptTokens)
loop(t,newScriptTokens)
}
}
loop(sigs,script)
}
/**
* 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
}
def parseScriptEither(scriptEither: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError]): Seq[ScriptToken] = scriptEither match {
case Left((_,scriptPubKey)) =>
logger.info("Script pubkey asm inside calculateForSigning: " + scriptPubKey.asm)
scriptPubKey.asm
case Right(_) => Nil //error
}
}

View file

@ -12,12 +12,4 @@ import org.scalatest.{FlatSpec, MustMatchers}
* Created by chris on 2/29/16.
*/
class TransactionSignatureCheckerTest extends FlatSpec with MustMatchers {
"TransactionSignatureChecker" must "remove the signatures from a p2sh scriptSig" in {
val p2shScriptSig = TestUtil.p2sh2Of3ScriptSig
val signatures = p2shScriptSig.signatures
val asmWithoutSigs = TransactionSignatureChecker.removeSignaturesFromScript(signatures,p2shScriptSig.asm)
val sigExists = signatures.map(sig => asmWithoutSigs.exists(_ == ScriptConstant(sig.hex)))
sigExists.exists(_ == true) must be (false)
}
}

View file

@ -13,7 +13,7 @@ import org.scalacheck.{Prop, Properties}
* Created by chris on 7/25/16.
*/
class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCreatorSpec") with BitcoinSLogger {
property("Must generate a valid signature for a p2pk transaction") =
/* property("Must generate a valid signature for a p2pk transaction") =
Prop.forAll(TransactionGenerators.signedP2PKTransaction) {
case (txSignatureComponent: TransactionSignatureComponent, _) =>
//run it through the interpreter
@ -93,5 +93,12 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
val result = ScriptInterpreter.run(program)
Seq(ScriptErrorUnsatisfiedLocktime, ScriptErrorPushSize).contains(result)
}*/
property("generate a valid signature for a witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedWitnessTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
}

View file

@ -143,4 +143,6 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val result = ScriptInterpreter.run(program)
result must be (ScriptOk)
}
}

View file

@ -213,5 +213,12 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey2,flags) must be (None)
}
it must "remove the signatures from a p2sh scriptSig" in {
val p2shScriptSig = TestUtil.p2sh2Of3ScriptSig
val signatures = p2shScriptSig.signatures
val asmWithoutSigs = BitcoinScriptUtil.removeSignaturesFromScript(signatures,p2shScriptSig.asm)
val sigExists = signatures.map(sig => asmWithoutSigs.exists(_ == ScriptConstant(sig.hex)))
sigExists.exists(_ == true) must be (false)
}
}