Nested OP_IF ScriptPubKey and signing (#857)

* Enabled nested conditional parsing

* Added nested conditionals to tests

* Responded to code review
This commit is contained in:
Nadav Kohen 2019-11-13 15:44:35 -06:00 committed by Chris Stewart
parent d86acfffed
commit 3c7fd6c34a
3 changed files with 249 additions and 134 deletions

View file

@ -4,7 +4,13 @@ import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.crypto._
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, _}
import org.bitcoins.core.script.control.{OP_ELSE, OP_ENDIF, OP_IF, OP_RETURN}
import org.bitcoins.core.script.control.{
OP_ELSE,
OP_ENDIF,
OP_IF,
OP_NOTIF,
OP_RETURN
}
import org.bitcoins.core.script.crypto.{
OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY,
@ -29,7 +35,11 @@ sealed abstract class ScriptPubKey extends Script
/** Trait for all Non-SegWit ScriptPubKeys */
sealed trait NonWitnessScriptPubKey extends ScriptPubKey
/** Trait for all raw, non-nested ScriptPubKeys (no P2SH) */
/** Trait for all raw, non-nested ScriptPubKeys (no P2SH)
*
* Note that all WitnessScriptPubKeys including P2WPKH are not
* considered to be non-nested and hence not RawScriptPubKeys.
*/
sealed trait RawScriptPubKey extends NonWitnessScriptPubKey
/**
@ -557,17 +567,18 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
/** Currently only supports a single OP_IF ... OP_ELSE ... OP_ENDIF ScriptPubKey */
sealed trait ConditionalScriptPubKey extends RawScriptPubKey {
require(asm.nonEmpty, "ConditionalScriptPubKey cannot be empty")
require(asm.head.equals(OP_IF),
require(asm.headOption.contains(OP_IF),
"ConditionalScriptPubKey must begin with OP_IF")
require(opElseIndex != -1,
"ConditionalScriptPubKey has to contain OP_ELSE asm token")
require(asm.last.equals(OP_ENDIF),
"ConditionalScriptPubKey must end in OP_ENDIF")
require(asm.count(_.equals(OP_IF)) == 1,
"ConditionalScriptPubKey does not currently support nesting OP_IFs")
require(asm.count(_.equals(OP_ELSE)) == 1,
"ConditionalScriptPubKey does not currently support nesting OP_ELSEs")
val (isValidConditional: Boolean, opElseIndex: Int) = {
ConditionalScriptPubKey.isConditionalScriptPubKeyWithElseIndex(asm)
}
require(isValidConditional, "Must be valid ConditionalScriptPubKey syntax")
require(opElseIndex != -1,
"ConditionalScriptPubKey has to contain OP_ELSE asm token")
require(!P2SHScriptPubKey.isP2SHScriptPubKey(trueSPK.asm) && !P2SHScriptPubKey
.isP2SHScriptPubKey(falseSPK.asm),
@ -579,10 +590,6 @@ sealed trait ConditionalScriptPubKey extends RawScriptPubKey {
"ConditionalScriptPubKey cannot wrap SegWit ScriptPubKey"
)
def opElseIndex: Int = {
asm.indexOf(OP_ELSE)
}
def trueSPK: RawScriptPubKey = {
RawScriptPubKey
.fromAsm(asm.slice(1, opElseIndex))
@ -620,17 +627,66 @@ object ConditionalScriptPubKey extends ScriptFactory[ConditionalScriptPubKey] {
fromAsm(asm)
}
def isConditionalScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
/** Validates the correctness of the conditional syntax.
* If valid, also returns the index of the first outer-most OP_ELSE
*/
def isConditionalScriptPubKeyWithElseIndex(
asm: Seq[ScriptToken]): (Boolean, Int) = {
val headIsOpIf = asm.headOption.contains(OP_IF)
lazy val containsOpElse = {
val opElseIndex = asm.indexOf(OP_ELSE)
opElseIndex != -1
}
lazy val singleOpIf = asm.count(_.equals(OP_IF)) == 1
lazy val singleOpElse = asm.count(_.equals(OP_ELSE)) == 1
lazy val endsWithEndIF = asm.last == OP_ENDIF
lazy val endsWithEndIf = asm.last == OP_ENDIF
headIsOpIf && containsOpElse && singleOpIf && singleOpElse && endsWithEndIF
var opElseIndexOpt: Option[Int] = None
lazy val validConditionalTree = {
// Already validate that the first token is OP_IF, go to tail
// and start with depth 1 and no OP_ELSE found for that depth
val opElsePendingOpt = asm.zipWithIndex.tail
.foldLeft[Option[Vector[Boolean]]](Some(Vector(false))) {
case (None, _) => // Invalid tree case, do no computation
None
case (Some(Vector()), _) => // Case of additional asm after final OP_ENDIF
None
case (Some(opElseFoundAtDepth), (OP_IF | OP_NOTIF, _)) =>
// Increase depth by one with OP_ELSE yet to be found for this depth
Some(opElseFoundAtDepth :+ false)
case (Some(opElseFoundAtDepth), (OP_ELSE, asmIndex)) =>
if (opElseFoundAtDepth == Vector(false)) {
// If first OP_ELSE at depth 1, set opElseIndex
opElseIndexOpt = Some(asmIndex)
}
if (opElseFoundAtDepth.last) {
// If OP_ELSE already found at this depth, invalid
None
} else {
// Otherwise, set to found at this depth
Some(
opElseFoundAtDepth.updated(opElseFoundAtDepth.length - 1, true))
}
case (Some(opElseFoundAtDepth), (OP_ENDIF, _)) =>
if (opElseFoundAtDepth.last) {
// If OP_ELSE found at this depth then valid, decrease depth by 1
Some(opElseFoundAtDepth.dropRight(1))
} else {
// Otherwise, invalid
None
}
case (Some(opElseFoundAtDepth), (_, _)) =>
// Token not related to conditional structure, ignore
Some(opElseFoundAtDepth)
}
// We should end on OP_ENDIF which will take us to depth 0
opElsePendingOpt.contains(Vector.empty)
}
lazy val opElseIndex = opElseIndexOpt.getOrElse(-1)
(headIsOpIf && endsWithEndIf && validConditionalTree, opElseIndex)
}
def isConditionalScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
isConditionalScriptPubKeyWithElseIndex(asm)._1
}
}

View file

@ -67,19 +67,22 @@ sealed abstract class UTXOSpendingInfo {
* If you over-specify a path, such as giving a condition where none is needed,
* then the remaining over-specified path will be ignored.
*
* Note that we do not yet support nested conditionals.
*
* For example, if you wanted to spend a ConditionalScriptPubKey(P2PK1, P2PK2)
* (which looks like OP_IF <P2PK1> OP_ELSE <P2PK2> OP_ENDIF) with the P2PK1 case,
* then you would construct a ConditionalSpendingInfo using ConditionTrue as your
* ConditionalPath. Otherwise if you wanted to use P2PK2 you would use ConditionFalse.
* then you would construct a ConditionalSpendingInfo using nonNestedTrue as your
* ConditionalPath. Otherwise if you wanted to use P2PK2 you would use nonNestedFalse.
*/
sealed trait ConditionalPath
object ConditionalPath {
case object NoConditionsLeft extends ConditionalPath
case object ConditionTrue extends ConditionalPath
case object ConditionFalse extends ConditionalPath
case class ConditionTrue(nextCondition: ConditionalPath)
extends ConditionalPath
case class ConditionFalse(nextCondition: ConditionalPath)
extends ConditionalPath
val nonNestedTrue: ConditionalPath = ConditionTrue(NoConditionsLeft)
val nonNestedFalse: ConditionalPath = ConditionFalse(NoConditionsLeft)
}
sealed trait BitcoinUTXOSpendingInfo extends UTXOSpendingInfo {
@ -352,15 +355,16 @@ case class ConditionalSpendingInfo(
require(conditionalPath != ConditionalPath.NoConditionsLeft,
"Must specify True or False")
val condition: Boolean = conditionalPath match {
case ConditionalPath.ConditionTrue =>
true
case ConditionalPath.ConditionFalse =>
false
case ConditionalPath.NoConditionsLeft =>
throw new IllegalStateException(
"This should be covered by invariant above")
}
val (condition: Boolean, nextConditionalPath: ConditionalPath) =
conditionalPath match {
case ConditionalPath.ConditionTrue(nextCondition) =>
(true, nextCondition)
case ConditionalPath.ConditionFalse(nextCondition) =>
(false, nextCondition)
case ConditionalPath.NoConditionsLeft =>
throw new IllegalStateException(
"This should be covered by invariant above")
}
val nestedSpendingInfo: RawScriptUTXOSpendingInfo = {
val nestedSPK = if (condition) {
@ -374,7 +378,7 @@ case class ConditionalSpendingInfo(
nestedSPK,
signers,
hashType,
ConditionalPath.NoConditionsLeft)
nextConditionalPath)
}
}

View file

@ -18,6 +18,7 @@ import org.bitcoins.core.wallet.utxo.{
}
import org.scalacheck.Gen
import scala.annotation.tailrec
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
@ -25,6 +26,7 @@ import scala.concurrent.duration.DurationInt
//TODO: Need to provide generators for [[NonStandardScriptSignature]] and [[NonStandardScriptPubKey]]
sealed abstract class ScriptGenerators extends BitcoinSLogger {
val timeout = 5.seconds
private val defaultMaxDepth = 2
def p2pkScriptSignature: Gen[P2PKScriptSignature] =
for {
@ -54,11 +56,9 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
.oneOf(
packageToSequenceOfPrivateKeys(signedP2PKHScriptSignature),
packageToSequenceOfPrivateKeys(signedP2PKScriptSignature),
signedMultiSignatureScriptSignature
/* Can't have these since that would create nested Conditional(Timelock(Conditional))
signedMultiSignatureScriptSignature,
signedCLTVScriptSignature,
signedCSVScriptSignature
*/
)
.map(_._1)
@ -121,37 +121,84 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2pkh = P2PKHScriptPubKey(pubKey)
} yield (p2pkh, privKey)
def cltvScriptPubKey: Gen[(CLTVScriptPubKey, Seq[ECPrivateKey])] =
def cltvScriptPubKey: Gen[(CLTVScriptPubKey, Seq[ECPrivateKey])] = {
cltvScriptPubKey(defaultMaxDepth)
}
def cltvScriptPubKey(
maxDepth: Int): Gen[(CLTVScriptPubKey, Seq[ECPrivateKey])] =
for {
num <- NumberGenerator.scriptNumbers
(cltv, privKeys, num) <- cltvScriptPubKey(num)
(cltv, privKeys, num) <- cltvScriptPubKey(num, maxDepth)
} yield (cltv, privKeys)
def cltvScriptPubKey(num: ScriptNumber): Gen[
(CLTVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] =
def cltvScriptPubKey(
num: ScriptNumber,
maxDepth: Int): Gen[(CLTVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] =
for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
(scriptPubKey, privKeys) <- nonLocktimeRawScriptPubKey(maxDepth - 1)
} yield {
val cltv = CLTVScriptPubKey(num, scriptPubKey)
(cltv, privKeys, num)
}
def csvScriptPubKey(num: ScriptNumber): Gen[
(CSVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] =
def nonConditionalCltvScriptPubKey: Gen[
(CLTVScriptPubKey, Seq[ECPrivateKey])] = {
for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
num <- NumberGenerator.scriptNumbers
(cltv, privKeys, num) <- nonConditionalCltvScriptPubKey(num)
} yield (cltv, privKeys)
}
def nonConditionalCltvScriptPubKey(num: ScriptNumber): Gen[
(CLTVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] =
for {
(scriptPubKey, privKeys) <- nonConditionalNonLocktimeRawScriptPubKey
} yield {
val cltv = CLTVScriptPubKey(num, scriptPubKey)
(cltv, privKeys, num)
}
def csvScriptPubKey: Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] = {
csvScriptPubKey(defaultMaxDepth)
}
def csvScriptPubKey(
num: ScriptNumber,
maxDepth: Int): Gen[(CSVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] =
for {
(scriptPubKey, privKeys) <- nonLocktimeRawScriptPubKey(maxDepth - 1)
} yield {
val csv = CSVScriptPubKey(num, scriptPubKey)
(csv, privKeys, num)
}
def csvScriptPubKey: Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] =
def csvScriptPubKey(
maxDepth: Int): Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] =
for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
(scriptPubKey, privKeys) <- nonLocktimeRawScriptPubKey(maxDepth - 1)
num <- NumberGenerator.scriptNumbers
csv = CSVScriptPubKey(num, scriptPubKey)
} yield (csv, privKeys)
def nonConditionalCsvScriptPubKey(num: ScriptNumber): Gen[
(CSVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] = {
for {
(scriptPubKey, privKeys) <- nonConditionalNonLocktimeRawScriptPubKey
} yield {
val csv = CSVScriptPubKey(num, scriptPubKey)
(csv, privKeys, num)
}
}
def nonConditionalCsvScriptPubKey: Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] = {
for {
(scriptPubKey, privKeys) <- nonConditionalNonLocktimeRawScriptPubKey
num <- NumberGenerator.scriptNumbers
csv = CSVScriptPubKey(num, scriptPubKey)
} yield (csv, privKeys)
}
def multiSigScriptPubKey: Gen[
(MultiSignatureScriptPubKey, Seq[ECPrivateKey])] =
for {
@ -179,29 +226,38 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
def emptyScriptPubKey: Gen[(EmptyScriptPubKey.type, Seq[ECPrivateKey])] =
(EmptyScriptPubKey, Nil)
/* We cannot have this one until there is support for nesting since
this allows Conditional(LockTime(Conditional))
/** Creates a ConditionalScriptPubKey with keys for the true case */
def conditionalScriptPubKey: Gen[
(ConditionalScriptPubKey, Seq[ECPrivateKey])] = {
nonConditionalRawScriptPubKey.flatMap {
case (spk1, keys1) =>
nonConditionalRawScriptPubKey.map(_._1).map { spk2 =>
(ConditionalScriptPubKey(spk1, spk2), keys1)
}
/** Creates a ConditionalScriptPubKey with keys for the true case
*
* @param maxDepth The maximum level of nesting allowed within this conditional.
*/
def conditionalScriptPubKey(
maxDepth: Int): Gen[(ConditionalScriptPubKey, Seq[ECPrivateKey])] = {
if (maxDepth > 0) {
for {
(spk1, keys1) <- rawScriptPubKey(maxDepth - 1)
(spk2, _) <- rawScriptPubKey(maxDepth - 1)
} yield (ConditionalScriptPubKey(spk1, spk2), keys1)
} else {
for {
(spk1, keys1) <- nonConditionalRawScriptPubKey
(spk2, _) <- nonConditionalRawScriptPubKey
} yield (ConditionalScriptPubKey(spk1, spk2), keys1)
}
}
*/
/** Creates a ConditionalScriptPubKey with keys for the true case */
def nonLocktimeConditionalScriptPubKey: Gen[
(ConditionalScriptPubKey, Seq[ECPrivateKey])] = {
nonConditionalNonLocktimeRawScriptPubKey.flatMap {
case (spk1, keys1) =>
nonConditionalNonLocktimeRawScriptPubKey.map(_._1).map { spk2 =>
(ConditionalScriptPubKey(spk1, spk2), keys1)
}
def nonLocktimeConditionalScriptPubKey(
maxDepth: Int): Gen[(ConditionalScriptPubKey, Seq[ECPrivateKey])] = {
if (maxDepth > 0) {
for {
(spk1, keys1) <- nonLocktimeRawScriptPubKey(maxDepth - 1)
(spk2, _) <- nonLocktimeRawScriptPubKey(maxDepth - 1)
} yield (ConditionalScriptPubKey(spk1, spk2), keys1)
} else {
for {
(spk1, keys1) <- nonConditionalNonLocktimeRawScriptPubKey
(spk2, _) <- nonConditionalNonLocktimeRawScriptPubKey
} yield (ConditionalScriptPubKey(spk1, spk2), keys1)
}
}
@ -250,29 +306,14 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq(_)),
p2pkhScriptPubKey.map(privKeyToSeq(_)),
cltvScriptPubKey.suchThat(
cltvScriptPubKey(defaultMaxDepth).suchThat(
!_._1.nestedScriptPubKey.isInstanceOf[CSVScriptPubKey]),
csvScriptPubKey.suchThat(
csvScriptPubKey(defaultMaxDepth).suchThat(
!_._1.nestedScriptPubKey.isInstanceOf[CLTVScriptPubKey]),
multiSigScriptPubKey,
p2wpkhSPKV0,
unassignedWitnessScriptPubKey,
nonLocktimeConditionalScriptPubKey
)
}
/**
* This is used for creating time locked scriptPubKeys, we cannot nest CSV/CLTV/P2SH/Witness
* ScriptPubKeys inside of timelock scriptPubKeys
*/
def randomNonLockTimeNonP2SHScriptPubKey: Gen[
(ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq(_)),
p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey,
nonLocktimeConditionalScriptPubKey,
emptyScriptPubKey
conditionalScriptPubKey(defaultMaxDepth)
)
}
@ -285,8 +326,13 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
nonLockTimeConditionalScriptSignature)
}
def lockTimeScriptPubKey: Gen[(LockTimeScriptPubKey, Seq[ECPrivateKey])] =
Gen.oneOf(cltvScriptPubKey, csvScriptPubKey)
def lockTimeScriptPubKey(
maxDepth: Int): Gen[(LockTimeScriptPubKey, Seq[ECPrivateKey])] =
Gen.oneOf(cltvScriptPubKey(maxDepth), csvScriptPubKey(maxDepth))
def nonConditionalLockTimeScriptPubKey: Gen[
(LockTimeScriptPubKey, Seq[ECPrivateKey])] =
Gen.oneOf(nonConditionalCltvScriptPubKey, nonConditionalCsvScriptPubKey)
def lockTimeScriptSig: Gen[LockTimeScriptSignature] =
Gen.oneOf(csvScriptSignature, cltvScriptSignature)
@ -298,14 +344,14 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey,
emptyScriptPubKey,
cltvScriptPubKey,
csvScriptPubKey,
cltvScriptPubKey(defaultMaxDepth),
csvScriptPubKey(defaultMaxDepth),
p2wpkhSPKV0,
p2wshSPKV0,
unassignedWitnessScriptPubKey,
p2shScriptPubKey,
witnessCommitment,
nonLocktimeConditionalScriptPubKey
conditionalScriptPubKey(defaultMaxDepth)
)
}
@ -315,10 +361,10 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2pkhScriptPubKey.map(privKeyToSeq),
multiSigScriptPubKey,
emptyScriptPubKey,
lockTimeScriptPubKey,
lockTimeScriptPubKey(defaultMaxDepth),
p2shScriptPubKey,
witnessCommitment,
nonLocktimeConditionalScriptPubKey
conditionalScriptPubKey(defaultMaxDepth)
)
}
@ -332,25 +378,40 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
)
}
def nonLocktimeRawScriptPubKey(
maxDepth: Int): Gen[(RawScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq),
p2pkhScriptPubKey.map(privKeyToSeq),
multiSigScriptPubKey,
emptyScriptPubKey,
nonLocktimeConditionalScriptPubKey(maxDepth)
)
}
def nonConditionalRawScriptPubKey: Gen[(RawScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq),
p2pkhScriptPubKey.map(privKeyToSeq),
multiSigScriptPubKey,
emptyScriptPubKey,
lockTimeScriptPubKey
nonConditionalLockTimeScriptPubKey
)
}
def rawScriptPubKey: Gen[(RawScriptPubKey, Seq[ECPrivateKey])] = {
rawScriptPubKey(defaultMaxDepth)
}
def rawScriptPubKey(
maxDepth: Int): Gen[(RawScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq),
p2pkhScriptPubKey.map(privKeyToSeq),
multiSigScriptPubKey,
emptyScriptPubKey,
lockTimeScriptPubKey,
witnessCommitment,
nonLocktimeConditionalScriptPubKey
lockTimeScriptPubKey(maxDepth),
conditionalScriptPubKey(maxDepth)
)
}
@ -512,12 +573,13 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
packageToSequenceOfPrivateKeys(signedP2PKScriptSignature),
signedMultiSignatureScriptSignature,
signedCLTVScriptSignature,
signedCSVScriptSignature
signedCSVScriptSignature,
signedConditionalScriptSignature
)
signed.flatMap {
case (scriptSig, spk, keys) =>
nonConditionalRawScriptPubKey.map(_._1).map { spk2 =>
rawScriptPubKey(defaultMaxDepth).map(_._1).map { spk2 =>
(ConditionalScriptSignature(scriptSig, true),
ConditionalScriptPubKey(spk.asInstanceOf[RawScriptPubKey], spk2),
keys)
@ -542,6 +604,25 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2SHScriptSignature = P2SHScriptSignature(scriptSig, redeemScript)
} yield (p2SHScriptSignature, p2SHScriptPubKey, privateKeys)
/** Utility function to compute how many signatures will be required in the inner-most true case.
* For use with CLTV and CSV ScriptSignature generation.
*/
@tailrec
private def findRequiredSigs(conditional: ConditionalScriptPubKey): Int = {
conditional.trueSPK match {
case multiSig: MultiSignatureScriptPubKey => multiSig.requiredSigs
case nestedConditional: ConditionalScriptPubKey =>
findRequiredSigs(nestedConditional)
case _: LockTimeScriptPubKey =>
throw new IllegalArgumentException(
"This shouldn't happen since we are using nonLocktimeRawScriptPubKey")
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey => 1
case EmptyScriptPubKey | _: WitnessCommitment |
_: NonStandardScriptPubKey =>
0
}
}
/**
* @return the signed `CLTVScriptSignature`, the
* `CLTVScriptPubKey` it spends, and the
@ -554,7 +635,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
sequence: UInt32): Gen[
(CLTVScriptSignature, CLTVScriptPubKey, Seq[ECPrivateKey])] =
for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
(scriptPubKey, privKeys) <- nonLocktimeRawScriptPubKey(defaultMaxDepth)
hashType <- CryptoGenerators.hashType
cltv = CLTVScriptPubKey(cltvLockTime, scriptPubKey)
} yield scriptPubKey match {
@ -568,24 +649,11 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case conditional: ConditionalScriptPubKey =>
val requiredSigs = conditional.trueSPK match {
case multiSig: MultiSignatureScriptPubKey => multiSig.requiredSigs
case _: ConditionalScriptPubKey =>
throw new IllegalStateException(
"No nesting of conditionals supported")
case _: LockTimeScriptPubKey =>
throw new IllegalArgumentException(
"This shouldn't happen since we are using randomNonLockTimeNonP2SHScriptPubKey")
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey => 1
case EmptyScriptPubKey | _: WitnessCommitment |
_: NonStandardScriptPubKey =>
0
}
val cltvScriptSig = lockTimeHelper(Some(lockTime),
sequence,
cltv,
privKeys,
Some(requiredSigs),
Some(findRequiredSigs(conditional)),
hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey =>
@ -628,7 +696,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
sequence: UInt32): Gen[
(CSVScriptSignature, CSVScriptPubKey, Seq[ECPrivateKey])] =
for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
(scriptPubKey, privKeys) <- nonLocktimeRawScriptPubKey(defaultMaxDepth)
hashType <- CryptoGenerators.hashType
csv = CSVScriptPubKey(csvScriptNum, scriptPubKey)
} yield scriptPubKey match {
@ -642,24 +710,11 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case conditional: ConditionalScriptPubKey =>
val requiredSigs = conditional.trueSPK match {
case multiSig: MultiSignatureScriptPubKey => multiSig.requiredSigs
case _: ConditionalScriptPubKey =>
throw new IllegalStateException(
"No nesting of conditionals supported")
case _: LockTimeScriptPubKey =>
throw new IllegalArgumentException(
"This shouldn't happen since we are using randomNonLockTimeNonP2SHScriptPubKey")
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey => 1
case EmptyScriptPubKey | _: WitnessCommitment |
_: NonStandardScriptPubKey =>
0
}
val csvScriptSig = lockTimeHelper(None,
sequence,
csv,
privKeys,
Some(requiredSigs),
Some(findRequiredSigs(conditional)),
hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey =>
@ -683,7 +738,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
def signedCSVScriptSignature: Gen[
(CSVScriptSignature, CSVScriptPubKey, Seq[ECPrivateKey])] =
for {
(csv, privKeys) <- csvScriptPubKey
(csv, privKeys) <- csvScriptPubKey(defaultMaxDepth)
sequence <- NumberGenerator.uInt32s
scriptSig <- signedCSVScriptSignature(csv.locktime, sequence)
} yield scriptSig
@ -691,7 +746,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
def signedCLTVScriptSignature: Gen[
(CLTVScriptSignature, CLTVScriptPubKey, Seq[ECPrivateKey])] =
for {
(cltv, privKeys) <- cltvScriptPubKey
(cltv, privKeys) <- cltvScriptPubKey(defaultMaxDepth)
txLockTime <- NumberGenerator.uInt32s
sequence <- NumberGenerator.uInt32s
scriptSig <- signedCLTVScriptSignature(cltv.locktime,