Wrote property to check all P2WSH(P2PK) txs can be created and verified

This commit is contained in:
Chris Stewart 2016-12-09 15:50:53 -06:00
parent a106d23610
commit 10892612e2
3 changed files with 59 additions and 40 deletions

View File

@ -30,32 +30,22 @@ trait DERSignatureUtil extends BitcoinSLogger {
else if (bytes.nonEmpty) {
//first byte must be 0x30
val firstByteIs0x30 = bytes.head == 0x30
logger.debug("firstByteIs0x30: " + firstByteIs0x30)
//second byte must indicate the length of the remaining byte array
val signatureSize = bytes(1).toLong
logger.debug("Encoded Sisgnature Size: " + signatureSize)
logger.debug("Actual Signature Size: " + bytes.slice(2,bytes.size).size)
//checks to see if the signature length is the same as the signatureSize val
val signatureLengthIsCorrect = signatureSize == bytes.slice(2,bytes.size).size
logger.debug("Signature length is correct: " + signatureLengthIsCorrect)
//third byte must be 0x02
val thirdByteIs0x02 = bytes(2) == 0x02
logger.debug("Third byte is 0x02: " + thirdByteIs0x02)
//this is the size of the r value in the signature
val rSize = bytes(3)
logger.debug("R size: " + rSize)
//r value in the signature
val r = bytes.slice(3,rSize+3)
logger.debug("R: " + BitcoinSUtil.encodeHex(r))
//this 0x02 separates the r and s value )in the signature
val second0x02Exists = bytes(rSize + 4) == 0x02
logger.debug("Second 0x02 exists: " + second0x02Exists)
//this is the size of the s value in the signature
val sSize = bytes(rSize + 4)
logger.debug("S Size: " + sSize)
val s = bytes.slice(rSize + 4 + 1, bytes.size)
logger.debug("S: " + BitcoinSUtil.encodeHex(s))
firstByteIs0x30 && signatureLengthIsCorrect && thirdByteIs0x02 &&
second0x02Exists
} else true
@ -153,65 +143,65 @@ trait DERSignatureUtil extends BitcoinSLogger {
if (bytes.size == 0) return true
if (bytes.size < 9) return false
logger.debug("signature is the minimum size for strict der encoding")
//logger.debug("signature is the minimum size for strict der encoding")
if (bytes.size > 73) return false
logger.debug("signature is under the maximum size for strict der encoding")
//logger.debug("signature is under the maximum size for strict der encoding")
// A signature is of type 0x30 (compound)
if (bytes.head != 0x30) return false
logger.debug("First byte is 0x30")
//logger.debug("First byte is 0x30")
// Make sure the length covers the entire signature.
if (bytes(1) != bytes.size - 3) return false
logger.debug("Signature length covers the entire signature")
//logger.debug("Signature length covers the entire signature")
val rSize = bytes(3)
logger.debug("rSize: " + rSize)
//logger.debug("rSize: " + rSize)
// Make sure the length of the S element is still inside the signature.
if (5 + rSize >= bytes.size) return false
logger.debug("Length of S element is contained in the signature")
//logger.debug("Length of S element is contained in the signature")
// Extract the length of the S element.
val sSize = bytes(5 + rSize)
logger.debug("sSize: " + sSize)
//logger.debug("sSize: " + sSize)
// Verify that the length of the signature matches the sum of the length
// of the elements.
if ((rSize + sSize + 7) != bytes.size) return false
logger.debug("Verify that the length of the signature matches the sum of the length of the elements.")
//logger.debug("Verify that the length of the signature matches the sum of the length of the elements.")
// Check whether the R element is an integer.
if (bytes(2) != 0x02) return false
logger.debug("R element is an integer")
//logger.debug("R element is an integer")
// Zero-length integers are not allowed for R.
if (rSize == 0) return false
logger.debug("r is not a zero length integer")
//logger.debug("r is not a zero length integer")
// Negative numbers are not allowed for R.
if ((bytes(4) & 0x80) != 0) return false
logger.debug("r is not a negative number")
//logger.debug("r is not a negative number")
// Null bytes at the start of R are not allowed, unless R would
// otherwise be interpreted as a negative number.
if (rSize > 1 && (bytes(4) == 0x00) && !((bytes(5) & 0x80) != 0 )) return false
logger.debug("There were not any null bytes at the start of R")
//logger.debug("There were not any null bytes at the start of R")
// Check whether the S element is an integer.
if (bytes(rSize + 4) != 0x02) return false
logger.debug("The S element is an integer")
//logger.debug("The S element is an integer")
// Zero-length integers are not allowed for S.
if (sSize == 0) return false
logger.debug("S was not a zero length integer")
//logger.debug("S was not a zero length integer")
// Negative numbers are not allowed for S.
if ((bytes(rSize + 6) & 0x80) != 0) return false
logger.debug("s was not a negative number")
//logger.debug("s was not a negative number")
// Null bytes at the start of S are not allowed, unless S would otherwise be
// interpreted as a negative number.
if (sSize > 1 && (bytes(rSize + 6) == 0x00) && !((bytes(rSize + 7) & 0x80) != 0)) return false
logger.debug("There were not any null bytes at the start of S")
//logger.debug("There were not any null bytes at the start of S")
//if we made it to this point without returning false this must be a valid strictly encoded der sig
true
}

View File

@ -1,6 +1,6 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECPublicKey, Sha256Hash160Digest}
import org.bitcoins.core.crypto.{ECPublicKey, HashDigest, Sha256Hash160Digest}
import org.bitcoins.core.number.Int64
import org.bitcoins.core.protocol._
import org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0.WitnessScriptPubKeyV0Impl
@ -32,6 +32,10 @@ sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger {
*/
lazy val asm : Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
/** The byte representation of [[asm]], this does NOT have the bytes
* for the [[org.bitcoins.core.protocol.CompactSizeUInt]] in the [[org.bitcoins.core.protocol.script.ScriptPubKey]]
*/
lazy val asmBytes: Seq[Byte] = asm.flatMap(_.bytes)
}
@ -560,8 +564,15 @@ object WitnessScriptPubKeyV0 {
}
/** Creates a P2WPKH witness script pubkey */
def apply(pubKey: ECPublicKey): WitnessScriptPubKeyV0 = {
val hash = CryptoUtil.sha256Hash160(pubKey.bytes)
def apply(pubKey: ECPublicKey): WitnessScriptPubKeyV0 = build(pubKey.bytes, CryptoUtil.sha256Hash160 _)
/** Creates a raw P2WSH script pubkey from the given [[ScriptPubKey]] */
def apply(scriptPubKey: ScriptPubKey): WitnessScriptPubKey = build(scriptPubKey.asmBytes, CryptoUtil.sha256 _)
/** Helper function to build [[WitnessScriptPubKey]], applies given hash function to the bytes,
* then places it inside of [[WitnessScriptPubKey]] */
private def build(bytes: Seq[Byte], hashFunc: Seq[Byte] => HashDigest): WitnessScriptPubKeyV0 = {
val hash = hashFunc(bytes)
val pushOp = BitcoinScriptUtil.calculatePushOp(hash.bytes)
WitnessScriptPubKeyV0(Seq(OP_0) ++ pushOp ++ Seq(ScriptConstant(hash.bytes)))
}
@ -592,4 +603,16 @@ object UnassignedWitnessScriptPubKey {
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(asmHex)
UnassignedWitnessScriptPubKeyImpl(compactSizeUInt.hex ++ asmHex)
}
}
}
/**
def buildScriptPubKey[T](asm: Seq[ScriptToken], constructor: String => T, invariant: Seq[ScriptToken] => Boolean, errorMsg: String) = {
if (invariant(asm)) {
val asmHex = asm.map(_.hex).mkString
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(asmHex)
constructor(compactSizeUInt.hex + asmHex)
} else throw new IllegalArgumentException(errorMsg)
}
*/

View File

@ -13,9 +13,9 @@ 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, _) =>
case (txSignatureComponent: TransactionSignatureComponent, _, _) =>
//run it through the interpreter
val program: PreExecutionScriptProgram = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
@ -24,7 +24,7 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
property("generate a valid signature for a p2pkh transaction") =
Prop.forAll(TransactionGenerators.signedP2PKHTransaction) {
case (txSignatureComponent: TransactionSignatureComponent, _) =>
case (txSignatureComponent: TransactionSignatureComponent, _, _) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
@ -33,18 +33,16 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
property("generate valid signatures for a multisignature transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedMultiSigTransaction) {
case (txSignatureComponent: TransactionSignatureComponent, _) =>
case (txSignatureComponent: TransactionSignatureComponent, _, _ ) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
property("generate a valid signature for a p2sh transaction") =
Prop.forAll(TransactionGenerators.signedP2SHTransaction) {
case (txSignatureComponent: TransactionSignatureComponent, _) =>
case (txSignatureComponent: TransactionSignatureComponent, _, _) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
@ -93,12 +91,20 @@ 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) =>
property("generate a valid signature for a p2wpkh witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WPKHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
property("generate a valid signature for a p2wsh(p2pk) witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WSHP2PKHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
}