Increase some Test Coverage (#866)

* Added ScriptTypeTest

* Added ScriptSignature coverage

* Added Signer test coverage

* Added test coverage for UTXOSpendingInfo

* Responded to code review
This commit is contained in:
Nadav Kohen 2019-11-15 11:21:52 -07:00 committed by Chris Stewart
parent b80a46ca9a
commit fad72c66d5
8 changed files with 185 additions and 67 deletions

View file

@ -0,0 +1,51 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.script.constant.{OP_FALSE, OP_TRUE}
import org.bitcoins.testkit.core.gen.{NumberGenerator, ScriptGenerators}
import org.bitcoins.testkit.util.BitcoinSUnitTest
class ConditionalScriptSignatureTest extends BitcoinSUnitTest {
behavior of "ConditionalScriptSignature"
implicit override val generatorDrivenConfig: PropertyCheckConfiguration = {
generatorDrivenConfigNewCode
}
it should "correctly read true and false" in {
val trueScriptSig = ConditionalScriptSignature.fromAsm(Vector(OP_TRUE))
val falseScriptSig = ConditionalScriptSignature.fromAsm(Vector(OP_FALSE))
assert(trueScriptSig.isTrue)
assert(!trueScriptSig.isFalse)
assert(!falseScriptSig.isTrue)
assert(falseScriptSig.isFalse)
assert(trueScriptSig.nestedScriptSig == EmptyScriptSignature)
assert(falseScriptSig.nestedScriptSig == EmptyScriptSignature)
}
it should "have serialization symmetry" in {
forAll(ScriptGenerators.conditionalScriptSignature) {
conditionalScriptSignature =>
assert(
ConditionalScriptSignature(conditionalScriptSignature.bytes) == conditionalScriptSignature)
}
}
it should "have agreement with nesting ScriptSignatures" in {
forAll(ScriptGenerators.scriptSignature, NumberGenerator.bool) {
case (scriptSig, condition) =>
val conditionalScriptSig =
ConditionalScriptSignature(scriptSig, condition)
assert(conditionalScriptSig.nestedScriptSig == scriptSig)
}
}
it should "have agreement with nested signatures" in {
forAll(ScriptGenerators.conditionalScriptSignature) {
conditionalScriptSignature =>
assert(
conditionalScriptSignature.signatures == conditionalScriptSignature.nestedScriptSig.signatures)
}
}
}

View file

@ -0,0 +1,29 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.testkit.core.gen.ScriptGenerators
import org.bitcoins.testkit.util.BitcoinSUnitTest
class LockTimeScriptSignatureTest extends BitcoinSUnitTest {
behavior of "LockTimeScriptSignature"
implicit override val generatorDrivenConfig: PropertyCheckConfiguration = {
generatorDrivenConfigNewCode
}
it should "have agreement with nesting ScriptSignatures" in {
forAll(ScriptGenerators.scriptSignature) { scriptSig =>
val csvScriptSig = CSVScriptSignature(scriptSig)
val cltvScriptSig = CLTVScriptSignature(scriptSig)
assert(csvScriptSig.scriptSig == scriptSig)
assert(cltvScriptSig.scriptSig == scriptSig)
}
}
it should "have agreement with nested signatures" in {
forAll(ScriptGenerators.lockTimeScriptSig) { lockTimeScriptSignature =>
assert(
lockTimeScriptSignature.signatures == lockTimeScriptSignature.scriptSig.signatures)
}
}
}

View file

@ -15,4 +15,10 @@ class MultiSignatureScriptSignatureTest extends BitcoinSUnitTest {
scriptSig.signatures.size must be(2)
}
it must "Fail validation if asm is empty" in {
assert(
!MultiSignatureScriptSignature.isMultiSignatureScriptSignature(
Vector.empty))
}
}

View file

@ -0,0 +1,22 @@
package org.bitcoins.core.script
import org.bitcoins.testkit.util.BitcoinSUnitTest
class ScriptTypeTest extends BitcoinSUnitTest {
behavior of "ScriptType"
it must "have serialization symmetry" in {
ScriptType.all.foreach { scriptType =>
val newScriptType = ScriptType.fromString(scriptType.toString)
assert(newScriptType.contains(scriptType))
}
}
it must "fail when nonsense ScriptType is used" in {
val lyrics = "Never gonna give you up, never gonna let you down"
assert(ScriptType.fromString(lyrics).isEmpty)
assertThrows[IllegalArgumentException](ScriptType.fromStringExn(lyrics))
}
}

View file

@ -0,0 +1,63 @@
package org.bitcoins.core.wallet.signer
import org.bitcoins.core.protocol.script.WitnessScriptPubKey
import org.bitcoins.core.wallet.utxo.{
P2SHSpendingInfo,
P2WPKHV0SpendingInfo,
P2WSHV0SpendingInfo,
UnassignedSegwitNativeUTXOSpendingInfo
}
import org.bitcoins.testkit.core.gen.{CreditingTxGen, TransactionGenerators}
import org.bitcoins.testkit.util.{BitcoinSAsyncTest, BitcoinSUnitTest}
import scala.concurrent.ExecutionContext
class SignerTest extends BitcoinSAsyncTest {
implicit val ec: ExecutionContext = ExecutionContext.global
behavior of "Signer"
it should "fail to sign a UnassignedSegwit UTXO" in {
val p2wpkh = CreditingTxGen.p2wpkhOutput.sample.get
val tx = TransactionGenerators.baseTransaction.sample.get
val spendingInfo = UnassignedSegwitNativeUTXOSpendingInfo(
p2wpkh.outPoint,
p2wpkh.amount,
p2wpkh.scriptPubKey.asInstanceOf[WitnessScriptPubKey],
p2wpkh.signers,
p2wpkh.hashType,
p2wpkh.scriptWitnessOpt.get,
p2wpkh.conditionalPath
)
assertThrows[UnsupportedOperationException](
BitcoinSigner.sign(spendingInfo, tx, isDummySignature = false))
}
it should "fail to sign a P2SH UTXO" in {
val p2sh = CreditingTxGen.p2shOutput.sample.get
val tx = TransactionGenerators.baseTransaction.sample.get
assertThrows[IllegalArgumentException](
BitcoinSigner.sign(p2sh, tx, isDummySignature = false))
}
it should "fail if there are inconsistent P2WPKH spending infos" in {
val dumbSpendingInfo = CreditingTxGen.output.sample.get
val p2wpkh =
CreditingTxGen.p2wpkhOutput.sample.get.asInstanceOf[P2WPKHV0SpendingInfo]
val tx = TransactionGenerators.baseTransaction.sample.get
recoverToSucceededIf[IllegalArgumentException] {
P2WPKHSigner.sign(dumbSpendingInfo, tx, isDummySignature = false, p2wpkh)
}
}
it should "fail if there are inconsistent P2WSH spending infos" in {
val dumbSpendingInfo = CreditingTxGen.output.sample.get
val p2wsh =
CreditingTxGen.p2wshOutput.sample.get.asInstanceOf[P2WSHV0SpendingInfo]
val tx = TransactionGenerators.baseTransaction.sample.get
recoverToSucceededIf[IllegalArgumentException] {
P2WSHSigner.sign(dumbSpendingInfo, tx, isDummySignature = false, p2wsh)
}
}
}

View file

@ -177,15 +177,6 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
sigs.map(s => ECDigitalSignature(s.hex))
}
/**
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script
*/
def splitAtRedeemScript: (Seq[ScriptToken], Seq[ScriptToken]) = {
(scriptSignatureNoRedeemScript.asm, redeemScript.asm)
}
override def toString = "P2SHScriptSignature(" + hex + ")"
}
@ -225,10 +216,8 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
}
/** Tests if the given asm tokens are a [[P2SHScriptSignature]] */
def isP2SHScriptSig(asm: Seq[ScriptToken]): Boolean = asm match {
case _ if asm.size > 1 && isRedeemScript(asm.last) => true
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => true
case _ => false
def isP2SHScriptSig(asm: Seq[ScriptToken]): Boolean = {
asm.size > 1 && isRedeemScript(asm.last)
}
/** Detects if the given script token is a redeem script */
@ -508,9 +497,7 @@ object ScriptSignature extends ScriptFactory[ScriptSignature] {
/** Creates a scriptSignature from the list of script tokens */
def fromAsm(tokens: Seq[ScriptToken]): ScriptSignature = tokens match {
case Nil => EmptyScriptSignature
case _
if (tokens.size > 1 && P2SHScriptSignature.isRedeemScript(
tokens.last)) =>
case _ if P2SHScriptSignature.isP2SHScriptSig(tokens) =>
P2SHScriptSignature.fromAsm(tokens)
case _ if ConditionalScriptSignature.isValidConditionalScriptSig(tokens) =>
ConditionalScriptSignature.fromAsm(tokens)
@ -524,41 +511,4 @@ object ScriptSignature extends ScriptFactory[ScriptSignature] {
P2PKScriptSignature.fromAsm(tokens)
case _ => NonStandardScriptSignature.fromAsm(tokens)
}
/**
* Creates a script signature from the given tokens and scriptPubKey
* @param tokens the script signature's tokens
* @param scriptPubKey the scriptPubKey which the script signature is trying to spend
* @return
*/
def fromScriptPubKey(
tokens: Seq[ScriptToken],
scriptPubKey: ScriptPubKey): Try[ScriptSignature] = scriptPubKey match {
case _: P2SHScriptPubKey => Try(P2SHScriptSignature.fromAsm(tokens))
case _: P2PKHScriptPubKey => Try(P2PKHScriptSignature.fromAsm(tokens))
case _: P2PKScriptPubKey => Try(P2PKScriptSignature.fromAsm(tokens))
case _: MultiSignatureScriptPubKey =>
Try(MultiSignatureScriptSignature.fromAsm(tokens))
case _: NonStandardScriptPubKey =>
Try(NonStandardScriptSignature.fromAsm(tokens))
case s: CLTVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case s: CSVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case _: ConditionalScriptPubKey =>
Try(ConditionalScriptSignature.fromAsm(tokens))
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey =>
Success(EmptyScriptSignature)
case EmptyScriptPubKey =>
if (tokens.isEmpty) Success(EmptyScriptSignature)
else Try(NonStandardScriptSignature.fromAsm(tokens))
case _: WitnessCommitment =>
Failure(
new IllegalArgumentException(
"Cannot spend witness commitment scriptPubKey"))
}
def apply(
tokens: Seq[ScriptToken],
scriptPubKey: ScriptPubKey): Try[ScriptSignature] = {
fromScriptPubKey(tokens, scriptPubKey)
}
}

View file

@ -9,7 +9,7 @@ package org.bitcoins.core.script
*/
sealed abstract class ScriptType {
import org.bitcoins.core.script.ScriptType._
override def toString = this match {
override def toString: String = this match {
case NONSTANDARD => "nonstandard"
case PUBKEY => "pubkey"
case PUBKEYHASH => "pubkeyhash"
@ -30,15 +30,15 @@ sealed abstract class ScriptType {
* from Bitcoin Core
*/
object ScriptType {
private val all: Seq[ScriptType] = Vector(NONSTANDARD,
PUBKEY,
PUBKEYHASH,
SCRIPTHASH,
MULTISIG,
NULLDATA,
WITNESS_V0_KEYHASH,
WITNESS_V0_SCRIPTHASH,
WITNESS_UNKNOWN)
private[script] val all: Seq[ScriptType] = Vector(NONSTANDARD,
PUBKEY,
PUBKEYHASH,
SCRIPTHASH,
MULTISIG,
NULLDATA,
WITNESS_V0_KEYHASH,
WITNESS_V0_SCRIPTHASH,
WITNESS_UNKNOWN)
def fromString(string: String): Option[ScriptType] =
all.find(_.toString == string)

View file

@ -348,10 +348,7 @@ sealed abstract class P2WPKHSigner extends BitcoinSigner[P2WPKHV0SpendingInfo] {
unsignedTxWitness)
val witSPK = output.scriptPubKey match {
case p2wpkh: P2WPKHWitnessSPKV0 =>
if (p2wpkh != P2WPKHWitnessSPKV0(pubKey)) {
Future.fromTry(TxBuilderError.WrongPublicKey)
} else Future.successful(p2wpkh)
case p2wpkh: P2WPKHWitnessSPKV0 => Future.successful(p2wpkh)
case _: UnassignedWitnessScriptPubKey | _: P2WSHWitnessSPKV0 =>
Future.fromTry(TxBuilderError.WrongSigner)
case _: NonWitnessScriptPubKey =>