mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 10:13:26 +01:00
Wrote property to check all P2WSH(P2PK) txs can be created and verified
This commit is contained in:
parent
a106d23610
commit
10892612e2
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
*/
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user