Create ScriptFactory.isValidAsm() to standardize how check validity o… (#2629)

* Create ScriptFactory.isValidAsm() to standardize how check validity of a script

* Fix compile

* Fix docs
This commit is contained in:
Chris Stewart 2021-02-06 08:43:17 -06:00 committed by GitHub
parent 593b1e2ce1
commit 097fa24e58
9 changed files with 144 additions and 155 deletions

View File

@ -17,15 +17,16 @@ class TxSigComponentTest extends BitcoinSUnitTest {
it should "correctly construct P2SHTxSigComponent" in {
val p2shNoNest = P2SHScriptPubKey(EmptyScriptPubKey)
val pubKey = ECPublicKey.freshPublicKey
val btx = BaseTransaction(
TransactionConstants.validLockVersion,
Vector(
TransactionInput(
TransactionOutPoint(DoubleSha256Digest.empty, UInt32.zero),
P2SHScriptSignature(ConditionalScriptSignature(EmptyScriptSignature,
condition = true),
EmptyScriptPubKey),
P2SHScriptSignature(scriptSig =
ConditionalScriptSignature(EmptyScriptSignature,
condition = true),
redeemScript = P2WPKHWitnessSPKV0(pubKey)),
TransactionConstants.sequence
)),
Vector(TransactionOutput(Satoshis.one, EmptyScriptPubKey)),

View File

@ -14,9 +14,7 @@ class MultiSignatureScriptSignatureTest extends BitcoinSUnitTest {
}
it must "Fail validation if asm is empty" in {
assert(
!MultiSignatureScriptSignature.isMultiSignatureScriptSignature(
Vector.empty))
assert(!MultiSignatureScriptSignature.isValidAsm(Vector.empty))
}
}

View File

@ -60,7 +60,7 @@ case class UTXORecord(
blockHash = blockHash)
case (path: NestedSegWitHDPath, Some(redeemScript), Some(scriptWitness))
if WitnessScriptPubKey.isWitnessScriptPubKey(redeemScript.asm) =>
if WitnessScriptPubKey.isValidAsm(redeemScript.asm) =>
NestedSegwitV0SpendingInfo(outpoint,
TransactionOutput(value, scriptPubKey),
path,

View File

@ -184,10 +184,7 @@ object TxSigComponent {
}
case _: P2SHScriptPubKey =>
val p2shScriptSig = scriptSig.asInstanceOf[P2SHScriptSignature]
if (
WitnessScriptPubKey.isWitnessScriptPubKey(
p2shScriptSig.redeemScript.asm)
) {
if (WitnessScriptPubKey.isValidAsm(p2shScriptSig.redeemScript.asm)) {
transaction match {
case _: NonWitnessTransaction =>
throw new IllegalArgumentException(

View File

@ -14,9 +14,8 @@ trait ScriptFactory[T <: Script] extends Factory[T] {
def buildScript(
asm: Vector[ScriptToken],
constructor: Vector[ScriptToken] => T,
invariant: Seq[ScriptToken] => Boolean,
errorMsg: String): T = {
if (invariant(asm)) {
if (isValidAsm(asm)) {
constructor(asm)
} else throw new IllegalArgumentException(errorMsg)
}
@ -47,4 +46,7 @@ trait ScriptFactory[T <: Script] extends Factory[T] {
def fromAsmHex(hex: String): T = {
fromAsmBytes(BytesUtil.decodeHex(hex))
}
/** Determines if the given asm is a valid T */
def isValidAsm(asm: Seq[ScriptToken]): Boolean
}

View File

@ -78,7 +78,6 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptPubKey = {
buildScript(asm.toVector,
P2PKHScriptPubKeyImpl.apply,
isP2PKHScriptPubKey,
"Given asm was not a p2pkh scriptPubKey, got: " + asm)
}
@ -87,7 +86,7 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
/** Checks if the given asm matches the pattern for
* [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey P2PKHScriptPubKey]]
*/
def isP2PKHScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
asm match {
case Seq(OP_DUP,
OP_HASH160,
@ -218,14 +217,13 @@ object MultiSignatureScriptPubKey
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = {
buildScript(asm.toVector,
MultiSignatureScriptPubKeyImpl.apply,
isMultiSignatureScriptPubKey,
"Given asm was not a MultSignatureScriptPubKey, got: " + asm)
}
def apply(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = fromAsm(asm)
/** Determines if the given script tokens are a multisignature `scriptPubKey` */
def isMultiSignatureScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val containsMultiSigOp =
asm.contains(OP_CHECKMULTISIG) || asm.contains(OP_CHECKMULTISIGVERIFY)
@ -321,7 +319,7 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
/** Checks if the given asm matches the pattern for
* [[org.bitcoins.core.protocol.script.P2SHScriptPubKey P2SHScriptPubKey]]
*/
def isP2SHScriptPubKey(asm: Seq[ScriptToken]): Boolean =
override def isValidAsm(asm: Seq[ScriptToken]): Boolean =
asm match {
case Seq(OP_HASH160,
_: BytesToPushOntoStack,
@ -334,7 +332,6 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptPubKey = {
buildScript(asm.toVector,
P2SHScriptPubKeyImpl.apply,
isP2SHScriptPubKey,
"Given asm was not a p2sh scriptPubkey, got: " + asm)
}
@ -371,7 +368,6 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptPubKey = {
buildScript(asm.toVector,
P2PKScriptPubKeyImpl.apply,
isP2PKScriptPubKey,
"Given asm was not a p2pk scriptPubKey, got: " + asm)
}
@ -380,7 +376,7 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
/** Sees if the given asm matches the
* [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey P2PKHScriptPubKey]] pattern
*/
def isP2PKScriptPubKey(asm: Seq[ScriptToken]): Boolean =
override def isValidAsm(asm: Seq[ScriptToken]): Boolean =
asm match {
case Seq(_: BytesToPushOntoStack, _: ScriptConstant, OP_CHECKSIG) => true
case _ => false
@ -416,7 +412,7 @@ sealed trait LockTimeScriptPubKey extends RawScriptPubKey {
object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): LockTimeScriptPubKey = {
require(isValidLockTimeScriptPubKey(asm))
require(isValidAsm(asm))
if (asm.contains(OP_CHECKLOCKTIMEVERIFY)) CLTVScriptPubKey(asm)
else if (asm.contains(OP_CHECKSEQUENCEVERIFY)) CSVScriptPubKey(asm)
else
@ -424,9 +420,9 @@ object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
"Given asm was not a LockTimeScriptPubKey, got: " + asm)
}
def isValidLockTimeScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
CLTVScriptPubKey.isCLTVScriptPubKey(asm) || CSVScriptPubKey
.isCSVScriptPubKey(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
CLTVScriptPubKey.isValidAsm(asm) || CSVScriptPubKey
.isValidAsm(asm)
}
}
@ -449,7 +445,6 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): CLTVScriptPubKey = {
buildScript(asm.toVector,
CLTVScriptPubKeyImpl.apply,
isCLTVScriptPubKey,
"Given asm was not a CLTVScriptPubKey, got: " + asm)
}
@ -474,13 +469,13 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
CLTVScriptPubKey(asm)
}
def isCLTVScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
if (asm.isEmpty) {
false
} else if (asm.head.isInstanceOf[BytesToPushOntoStack]) {
val tailTokens = asm.slice(4, asm.length)
if (
P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens
P2SHScriptPubKey.isValidAsm(tailTokens) || tailTokens
.contains(OP_CHECKLOCKTIMEVERIFY)
) return false
asm.slice(0, 4) match {
@ -494,7 +489,7 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
} else {
val tailTokens = asm.slice(3, asm.length)
if (
P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens
P2SHScriptPubKey.isValidAsm(tailTokens) || tailTokens
.contains(OP_CHECKLOCKTIMEVERIFY)
) return false
asm.slice(0, 3) match {
@ -541,7 +536,6 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): CSVScriptPubKey = {
buildScript(asm.toVector,
CSVScriptPubKeyImpl.apply,
isCSVScriptPubKey,
"Given asm was not a CSVScriptPubKey, got: " + asm)
}
@ -568,11 +562,11 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
CSVScriptPubKey(asm)
}
def isCSVScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
if (asm.head.isInstanceOf[BytesToPushOntoStack]) {
val tailTokens = asm.slice(4, asm.length)
if (
P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens
P2SHScriptPubKey.isValidAsm(tailTokens) || tailTokens
.contains(OP_CHECKSEQUENCEVERIFY)
) return false
asm.slice(0, 4) match {
@ -586,7 +580,7 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
} else {
val tailTokens = asm.slice(3, asm.length)
if (
P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens
P2SHScriptPubKey.isValidAsm(tailTokens) || tailTokens
.contains(OP_CHECKSEQUENCEVERIFY)
) return false
asm.slice(0, 3) match {
@ -619,13 +613,13 @@ sealed trait ConditionalScriptPubKey extends RawScriptPubKey {
val opElseIndex: Int = opElseIndexOpt.get
require(!P2SHScriptPubKey.isP2SHScriptPubKey(trueSPK.asm) && !P2SHScriptPubKey
.isP2SHScriptPubKey(falseSPK.asm),
require(!P2SHScriptPubKey.isValidAsm(trueSPK.asm) && !P2SHScriptPubKey
.isValidAsm(falseSPK.asm),
"ConditionalScriptPubKey cannot wrap P2SH")
require(
!WitnessScriptPubKey
.isWitnessScriptPubKey(trueSPK.asm) && !WitnessScriptPubKey
.isWitnessScriptPubKey(falseSPK.asm),
.isValidAsm(trueSPK.asm) && !WitnessScriptPubKey
.isValidAsm(falseSPK.asm),
"ConditionalScriptPubKey cannot wrap SegWit ScriptPubKey"
)
@ -754,7 +748,6 @@ object NonStandardIfConditionalScriptPubKey
buildScript(
asm = asm.toVector,
constructor = NonStandardIfConditionalScriptPubKeyImpl.apply,
invariant = isNonStandardIfConditionalScriptPubKey,
errorMsg =
"Given asm was not a NonStandardIfConditionalScriptPubKey, got: " + asm
)
@ -770,13 +763,13 @@ object NonStandardIfConditionalScriptPubKey
fromAsm(asm)
}
def isNonStandardIfConditionalScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val validIf = ConditionalScriptPubKey
.isConditionalScriptPubKeyWithElseIndex(asm, OP_IF)
._1
val isMultiSigWithTimeout = MultiSignatureWithTimeoutScriptPubKey
.isMultiSignatureWithTimeoutScriptPubKey(asm)
.isValidAsm(asm)
validIf && !isMultiSigWithTimeout
}
@ -784,10 +777,9 @@ object NonStandardIfConditionalScriptPubKey
sealed trait MultiSignatureWithTimeoutScriptPubKey
extends IfConditionalScriptPubKey {
require(
MultiSignatureScriptPubKey.isMultiSignatureScriptPubKey(super.firstSPK.asm),
"True case must be MultiSignatureSPK")
require(CLTVScriptPubKey.isCLTVScriptPubKey(super.secondSPK.asm),
require(MultiSignatureScriptPubKey.isValidAsm(super.firstSPK.asm),
"True case must be MultiSignatureSPK")
require(CLTVScriptPubKey.isValidAsm(super.secondSPK.asm),
"False case must be CLTVSPK")
override def firstSPK: MultiSignatureScriptPubKey = {
@ -826,7 +818,6 @@ object MultiSignatureWithTimeoutScriptPubKey
buildScript(
asm = asm.toVector,
constructor = MultiSignatureWithTimeoutScriptPubKeyImpl.apply,
invariant = isMultiSignatureWithTimeoutScriptPubKey,
errorMsg =
s"Given asm was not a MultiSignatureWithTimeoutScriptPubKey, got: $asm"
)
@ -841,17 +832,15 @@ object MultiSignatureWithTimeoutScriptPubKey
fromAsm(asm)
}
def isMultiSignatureWithTimeoutScriptPubKey(
asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val (validIf, opElseIndexOpt) = ConditionalScriptPubKey
.isConditionalScriptPubKeyWithElseIndex(asm, OP_IF)
lazy val validMultiSigWithCLTV = opElseIndexOpt match {
case Some(opElseIndex) =>
MultiSignatureScriptPubKey
.isMultiSignatureScriptPubKey(
asm.slice(1, opElseIndex)) && CLTVScriptPubKey
.isCLTVScriptPubKey(asm.slice(opElseIndex + 1, asm.length - 1))
.isValidAsm(asm.slice(1, opElseIndex)) && CLTVScriptPubKey
.isValidAsm(asm.slice(opElseIndex + 1, asm.length - 1))
case _ => false
}
@ -879,7 +868,6 @@ object NonStandardNotIfConditionalScriptPubKey
buildScript(
asm = asm.toVector,
constructor = NonStandardNotIfConditionalScriptPubKeyImpl.apply,
invariant = isNonStandardNotIfConditionalScriptPubKey,
errorMsg = "Given asm was not a NotIfConditionalScriptPubKey, got: " + asm
)
}
@ -893,8 +881,7 @@ object NonStandardNotIfConditionalScriptPubKey
fromAsm(asm)
}
def isNonStandardNotIfConditionalScriptPubKey(
asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
ConditionalScriptPubKey
.isConditionalScriptPubKeyWithElseIndex(asm, OP_NOTIF)
._1
@ -948,7 +935,6 @@ object P2PKWithTimeoutScriptPubKey
buildScript(
asm = asm.toVector,
constructor = P2PKWithTimeoutScriptPubKeyImpl.apply,
invariant = isP2PKWithTimeoutScriptPubKey,
errorMsg = s"Given asm was not a P2PKWithTimeoutScriptPubKey, got $asm"
)
}
@ -975,7 +961,7 @@ object P2PKWithTimeoutScriptPubKey
)
}
def isP2PKWithTimeoutScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
if (asm.length < 5) {
false
} else {
@ -1025,15 +1011,12 @@ object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptPubKey = {
//everything can be a NonStandardScriptPubkey, thus the trivially true function
buildScript(asm.toVector,
NonStandardScriptPubKeyImpl.apply,
{ _ =>
true
},
"")
buildScript(asm.toVector, NonStandardScriptPubKeyImpl.apply, "")
}
def apply(asm: Seq[ScriptToken]): NonStandardScriptPubKey = fromAsm(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = true
}
/** Represents the empty ScriptPubKey */
@ -1049,43 +1032,46 @@ object RawScriptPubKey extends ScriptFactory[RawScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): RawScriptPubKey =
asm match {
case Nil => EmptyScriptPubKey
case _
if P2PKWithTimeoutScriptPubKey.isP2PKWithTimeoutScriptPubKey(asm) =>
case _ if P2PKWithTimeoutScriptPubKey.isValidAsm(asm) =>
P2PKWithTimeoutScriptPubKey.fromAsm(asm)
case _
if MultiSignatureWithTimeoutScriptPubKey
.isMultiSignatureWithTimeoutScriptPubKey(asm) =>
.isValidAsm(asm) =>
MultiSignatureWithTimeoutScriptPubKey.fromAsm(asm)
case _
if NonStandardIfConditionalScriptPubKey
.isNonStandardIfConditionalScriptPubKey(asm) =>
.isValidAsm(asm) =>
NonStandardIfConditionalScriptPubKey.fromAsm(asm)
case _
if NonStandardNotIfConditionalScriptPubKey
.isNonStandardNotIfConditionalScriptPubKey(asm) =>
.isValidAsm(asm) =>
NonStandardNotIfConditionalScriptPubKey.fromAsm(asm)
case _ if P2PKHScriptPubKey.isP2PKHScriptPubKey(asm) =>
case _ if P2PKHScriptPubKey.isValidAsm(asm) =>
P2PKHScriptPubKey(asm)
case _ if P2PKScriptPubKey.isP2PKScriptPubKey(asm) =>
case _ if P2PKScriptPubKey.isValidAsm(asm) =>
P2PKScriptPubKey(asm)
case _ if MultiSignatureScriptPubKey.isMultiSignatureScriptPubKey(asm) =>
case _ if MultiSignatureScriptPubKey.isValidAsm(asm) =>
MultiSignatureScriptPubKey(asm)
case _ if CLTVScriptPubKey.isCLTVScriptPubKey(asm) =>
case _ if CLTVScriptPubKey.isValidAsm(asm) =>
CLTVScriptPubKey(asm)
case _ if CSVScriptPubKey.isCSVScriptPubKey(asm) => CSVScriptPubKey(asm)
case _ if WitnessCommitment.isWitnessCommitment(asm) =>
case _ if CSVScriptPubKey.isValidAsm(asm) => CSVScriptPubKey(asm)
case _ if WitnessCommitment.isValidAsm(asm) =>
WitnessCommitment(asm)
case _ => NonStandardScriptPubKey(asm)
}
def apply(asm: Seq[ScriptToken]): RawScriptPubKey = fromAsm(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
!WitnessScriptPubKey.isValidAsm(asm) && !P2SHScriptPubKey.isValidAsm(asm)
}
}
object NonWitnessScriptPubKey extends ScriptFactory[NonWitnessScriptPubKey] {
val empty: NonWitnessScriptPubKey = fromAsm(Nil)
def fromAsm(asm: Seq[ScriptToken]): NonWitnessScriptPubKey = {
if (P2SHScriptPubKey.isP2SHScriptPubKey(asm)) {
if (P2SHScriptPubKey.isValidAsm(asm)) {
P2SHScriptPubKey(asm)
} else {
RawScriptPubKey.fromAsm(asm)
@ -1093,6 +1079,10 @@ object NonWitnessScriptPubKey extends ScriptFactory[NonWitnessScriptPubKey] {
}
def apply(asm: Seq[ScriptToken]): NonWitnessScriptPubKey = fromAsm(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
!WitnessScriptPubKey.isValidAsm(asm)
}
}
/** Factory companion object used to create
@ -1102,12 +1092,12 @@ object ScriptPubKey extends ScriptFactory[ScriptPubKey] {
val empty: ScriptPubKey = fromAsm(Nil)
/** Creates a `scriptPubKey` from its asm representation */
def fromAsm(asm: Seq[ScriptToken]): ScriptPubKey = {
override def fromAsm(asm: Seq[ScriptToken]): ScriptPubKey = {
val nonWitnessScriptPubKey = NonWitnessScriptPubKey.fromAsm(asm)
if (
nonWitnessScriptPubKey
.isInstanceOf[NonStandardScriptPubKey] && WitnessScriptPubKey
.isWitnessScriptPubKey(asm)
.isValidAsm(asm)
) {
WitnessScriptPubKey(asm)
} else {
@ -1116,6 +1106,9 @@ object ScriptPubKey extends ScriptFactory[ScriptPubKey] {
}
def apply(asm: Seq[ScriptToken]): ScriptPubKey = fromAsm(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = true
}
/** This type represents a
@ -1156,11 +1149,11 @@ object WitnessScriptPubKey extends ScriptFactory[WitnessScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): WitnessScriptPubKey =
asm match {
case _ if P2WPKHWitnessSPKV0.isValid(asm) =>
case _ if P2WPKHWitnessSPKV0.isValidAsm(asm) =>
P2WPKHWitnessSPKV0.fromAsm(asm)
case _ if P2WSHWitnessSPKV0.isValid(asm) =>
case _ if P2WSHWitnessSPKV0.isValidAsm(asm) =>
P2WSHWitnessSPKV0.fromAsm(asm)
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) =>
case _ if WitnessScriptPubKey.isValidAsm(asm) =>
UnassignedWitnessScriptPubKey(asm)
case _ =>
throw new IllegalArgumentException(
@ -1173,7 +1166,7 @@ object WitnessScriptPubKey extends ScriptFactory[WitnessScriptPubKey] {
* [[https://github.com/bitcoin/bitcoin/blob/14d01309bed59afb08651f2b701ff90371b15b20/src/script/script.cpp#L223-L237 this function]]
* inside of Bitcoin Core
*/
def isWitnessScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
//multisig spk with zero public keys
//OP_0, BytesToPushOntoStackImpl(3), ScriptConstantImpl(ByteVector(3 bytes, 0x0000ae)
@ -1188,8 +1181,8 @@ object WitnessScriptPubKey extends ScriptFactory[WitnessScriptPubKey] {
val firstOp = asm.headOption
if (bytes.size < 4 || bytes.size > 42) false
else if (!validWitVersions.contains(firstOp.getOrElse(OP_1NEGATE))) false
else if (MultiSignatureScriptPubKey.isMultiSignatureScriptPubKey(asm)) false
else if (LockTimeScriptPubKey.isValidLockTimeScriptPubKey(asm)) false
else if (MultiSignatureScriptPubKey.isValidAsm(asm)) false
else if (LockTimeScriptPubKey.isValidAsm(asm)) false
else if (asm(1).toLong + 2 == bytes.size) true
else false
}
@ -1212,18 +1205,17 @@ object WitnessScriptPubKeyV0 extends ScriptFactory[WitnessScriptPubKeyV0] {
* Verison 0 witness program need to have an OP_0 as the first operation
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpp#L215-L229]]
*/
def isValid(asm: Seq[ScriptToken]): Boolean = {
asm.headOption.contains(OP_0) && WitnessScriptPubKey.isWitnessScriptPubKey(
asm)
def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
asm.headOption.contains(OP_0) && WitnessScriptPubKey.isValidAsm(asm)
}
def apply(asm: Seq[ScriptToken]): WitnessScriptPubKeyV0 = fromAsm(asm)
override def fromAsm(asm: Seq[ScriptToken]): WitnessScriptPubKeyV0 =
asm match {
case _ if P2WPKHWitnessSPKV0.isValid(asm) =>
case _ if P2WPKHWitnessSPKV0.isValidAsm(asm) =>
P2WPKHWitnessSPKV0.fromAsm(asm)
case _ if P2WSHWitnessSPKV0.isValid(asm) =>
case _ if P2WSHWitnessSPKV0.isValidAsm(asm) =>
P2WSHWitnessSPKV0.fromAsm(asm)
case _ =>
sys.error(
@ -1250,14 +1242,12 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
override def fromAsm(asm: Seq[ScriptToken]): P2WPKHWitnessSPKV0 = {
buildScript(asm.toVector,
P2WPKHWitnessSPKV0Impl.apply,
isValid,
s"Given asm was not a P2WPKHWitnessSPKV0, got $asm")
}
def isValid(asm: Seq[ScriptToken]): Boolean = {
def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val asmBytes = BytesUtil.toByteVector(asm)
asmBytes.size == 22 && WitnessScriptPubKeyV0.isValid(asm)
asmBytes.size == 22 && WitnessScriptPubKeyV0.isValidAsm(asm)
}
def fromHash(hash: Sha256Hash160Digest): P2WPKHWitnessSPKV0 = {
@ -1296,13 +1286,12 @@ object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
override def fromAsm(asm: Seq[ScriptToken]): P2WSHWitnessSPKV0 = {
buildScript(asm.toVector,
P2WSHWitnessSPKV0Impl.apply,
isValid,
s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
}
def isValid(asm: Seq[ScriptToken]): Boolean = {
def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val asmBytes = BytesUtil.toByteVector(asm)
WitnessScriptPubKeyV0.isValid(asm) &&
WitnessScriptPubKeyV0.isValidAsm(asm) &&
asmBytes.size == 34
}
@ -1344,11 +1333,14 @@ object UnassignedWitnessScriptPubKey
buildScript(
asm.toVector,
UnassignedWitnessScriptPubKeyImpl.apply,
WitnessScriptPubKey.isWitnessScriptPubKey,
"Given asm was not a valid witness script pubkey: " + asm
)
}
def apply(asm: Seq[ScriptToken]): UnassignedWitnessScriptPubKey = fromAsm(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
WitnessScriptPubKey.isValidAsm(asm)
}
}
/** This trait represents the witness commitment found in the coinbase transaction
@ -1389,7 +1381,6 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
override def fromAsm(asm: Seq[ScriptToken]): WitnessCommitment = {
buildScript(asm.toVector,
WitnessCommitmentImpl.apply,
isWitnessCommitment,
"Given asm was not a valid witness commitment, got: " + asm)
}
@ -1403,7 +1394,7 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
/** This determines if the given asm has the correct witness structure according to
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure BIP141]]
*/
def isWitnessCommitment(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
if (asm.size < 3) false
else {
val minCommitmentSize = 38

View File

@ -45,11 +45,12 @@ object NonStandardScriptSignature
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptSignature = {
buildScript(asm = asm.toVector,
constructor = NonStandardScriptSignatureImpl(_),
invariant = { _ =>
true
},
errorMsg = "")
}
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
true
}
}
/** A script signature to be used in tests for signing EmptyScriptPubKey.
@ -61,7 +62,7 @@ case object TrivialTrueScriptSignature extends ScriptSignature {
override lazy val asm: Vector[ScriptToken] =
Vector(OP_TRUE)
def isTrivialTrueScriptSignature(asm: Seq[ScriptToken]): Boolean = {
def isValid(asm: Seq[ScriptToken]): Boolean = {
asm == this.asm
}
}
@ -97,7 +98,6 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
buildScript(
asm = asm.toVector,
constructor = P2PKHScriptSignatureImpl(_),
invariant = isP2PKHScriptSig(_),
errorMsg = s"Given asm was not a P2PKHScriptSignature, got: $asm"
)
}
@ -119,7 +119,7 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
}
/** Determines if the given asm matches a [[P2PKHScriptSignature]] */
def isP2PKHScriptSig(asm: Seq[ScriptToken]): Boolean =
override def isValidAsm(asm: Seq[ScriptToken]): Boolean =
asm match {
case Seq(_: BytesToPushOntoStack,
maybeSig: ScriptConstant,
@ -148,7 +148,7 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
EmptyScriptPubKey
} else if (
scriptSig == EmptyScriptSignature &&
WitnessScriptPubKey.isWitnessScriptPubKey(asm.tail)
WitnessScriptPubKey.isValidAsm(asm.tail)
) {
//if we have an EmptyScriptSignature, we need to check if the rest of the asm
//is a Witness script. It is not necessarily a witness script, since this code
@ -163,7 +163,7 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
/** Returns the script signature of this p2shScriptSig with no serialized redeemScript */
def scriptSignatureNoRedeemScript: ScriptSignature = {
//witness scriptPubKeys always have EmptyScriptSigs
if (WitnessScriptPubKey.isWitnessScriptPubKey(asm)) {
if (WitnessScriptPubKey.isValidAsm(asm)) {
EmptyScriptSignature
} else {
val asmWithoutRedeemScriptAndPushOp: Try[Seq[ScriptToken]] = Try {
@ -222,23 +222,33 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
P2SHScriptSignature(EmptyScriptSignature, witnessScriptPubKey)
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptSignature = {
override def buildScript(
asm: Vector[ScriptToken],
constructor: Vector[ScriptToken] => P2SHScriptSignature,
errorMsg: String): P2SHScriptSignature = {
//everything can be a P2SHScriptSignature, thus passing the trivially true function
//the most important thing to note is we cannot have a P2SHScriptSignature unless
//we have a P2SHScriptPubKey
//previously P2SHScriptSignature's redeem script had to be standard scriptPubKey's, this
//was removed in 0.11 or 0.12 of Bitcoin Core
constructor(asm)
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptSignature = {
buildScript(asm = asm.toVector,
constructor = P2SHScriptSignatureImpl(_),
invariant = { _ =>
true
},
errorMsg =
s"Given asm tokens are not a p2sh scriptSig, got: $asm")
}
/** Tests if the given asm tokens are a [[P2SHScriptSignature]] */
def isP2SHScriptSig(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
//as noted above, techinically _anything_ can be a p2sh scriptsig
//this applies basic checks to see if it's a standard redeemScript
//rather than a non standard redeeScript.
//this will return false if the redeemScript is not a
//supported scriptpubkey type in our library
asm.size > 1 && isRedeemScript(asm.last)
}
@ -323,7 +333,6 @@ object MultiSignatureScriptSignature
buildScript(
asm = asm.toVector,
constructor = MultiSignatureScriptSignatureImpl(_),
invariant = isMultiSignatureScriptSignature(_),
errorMsg =
s"The given asm tokens were not a multisignature script sig: $asm"
)
@ -335,7 +344,7 @@ object MultiSignatureScriptSignature
* @param asm the asm to check if it falls in the multisignature script sig format
* @return boolean indicating if the scriptsignature is a multisignature script signature
*/
def isMultiSignatureScriptSignature(asm: Seq[ScriptToken]): Boolean =
override def isValidAsm(asm: Seq[ScriptToken]): Boolean =
asm.isEmpty match {
case true => false
//case false if (asm.size == 1) => false
@ -394,12 +403,11 @@ object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptSignature = {
buildScript(asm.toVector,
P2PKScriptSignatureImpl(_),
isP2PKScriptSignature(_),
"The given asm tokens were not a p2pk script sig: " + asm)
}
/** P2PK scriptSigs always have the pattern [pushop, digitalSignature] */
def isP2PKScriptSignature(asm: Seq[ScriptToken]): Boolean =
override def isValidAsm(asm: Seq[ScriptToken]): Boolean =
asm match {
case Seq(_: BytesToPushOntoStack, _: ScriptConstant) => true
case _ => false
@ -413,7 +421,6 @@ object P2PKWithTimeoutScriptSignature
buildScript(
asm.toVector,
ConditionalScriptSignature.fromAsm,
isP2PKWithTimeoutScriptSignature,
s"The given asm tokens were not a P2PKWithTimeoutScriptSignature, got $asm"
)
}
@ -424,10 +431,10 @@ object P2PKWithTimeoutScriptSignature
ConditionalScriptSignature(P2PKScriptSignature(signature), beforeTimeout)
}
def isP2PKWithTimeoutScriptSignature(asm: Seq[ScriptToken]): Boolean = {
P2PKScriptSignature.isP2PKScriptSignature(
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
P2PKScriptSignature.isValidAsm(
asm.dropRight(1)) && ConditionalScriptSignature
.isValidConditionalScriptSig(asm)
.isValidAsm(asm)
}
}
@ -455,9 +462,6 @@ object CLTVScriptSignature extends ScriptFactory[CLTVScriptSignature] {
override def fromAsm(asm: Seq[ScriptToken]): CLTVScriptSignature = {
buildScript(asm = asm.toVector,
constructor = CLTVScriptSignatureImpl(_),
invariant = { _ =>
true
},
errorMsg = s"Given asm was not a CLTVScriptSignature $asm")
}
@ -468,6 +472,10 @@ object CLTVScriptSignature extends ScriptFactory[CLTVScriptSignature] {
def apply(scriptSig: ScriptSignature): CLTVScriptSignature = {
fromHex(scriptSig.hex)
}
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
true
}
}
sealed trait CSVScriptSignature extends LockTimeScriptSignature {
@ -483,9 +491,6 @@ object CSVScriptSignature extends ScriptFactory[CSVScriptSignature] {
override def fromAsm(asm: Seq[ScriptToken]): CSVScriptSignature = {
buildScript(asm = asm.toVector,
constructor = CSVScriptSignatureImpl(_),
invariant = { _ =>
true
},
errorMsg = s"Given asm was not a CLTVScriptSignature $asm")
}
@ -496,11 +501,13 @@ object CSVScriptSignature extends ScriptFactory[CSVScriptSignature] {
def apply(scriptSig: ScriptSignature): CSVScriptSignature = {
fromHex(scriptSig.hex)
}
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = true
}
/** ScriptSignature for both OP_IF and OP_NOTIF ScriptPubKeys */
sealed trait ConditionalScriptSignature extends ScriptSignature {
require(ConditionalScriptSignature.isValidConditionalScriptSig(asm),
require(ConditionalScriptSignature.isValidAsm(asm),
"ConditionalScriptSignature must end in true or false")
def isTrue: Boolean = {
@ -535,7 +542,6 @@ object ConditionalScriptSignature
override def fromAsm(asm: Seq[ScriptToken]): ConditionalScriptSignature = {
buildScript(asm.toVector,
ConditionalScriptSignatureImpl.apply,
isValidConditionalScriptSig,
s"Given asm was not a ConditionalScriptSignature: $asm")
}
@ -573,7 +579,7 @@ object ConditionalScriptSignature
}
}
def isValidConditionalScriptSig(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
asm.lastOption.exists(Vector(OP_0, OP_FALSE, OP_1, OP_TRUE).contains)
}
}
@ -593,22 +599,20 @@ object ScriptSignature extends ScriptFactory[ScriptSignature] {
def fromAsm(tokens: Seq[ScriptToken]): ScriptSignature =
tokens match {
case Nil => EmptyScriptSignature
case _
if TrivialTrueScriptSignature.isTrivialTrueScriptSignature(tokens) =>
case _ if TrivialTrueScriptSignature.isValid(tokens) =>
TrivialTrueScriptSignature
case _ if P2SHScriptSignature.isP2SHScriptSig(tokens) =>
case _ if P2SHScriptSignature.isValidAsm(tokens) =>
P2SHScriptSignature.fromAsm(tokens)
case _
if ConditionalScriptSignature.isValidConditionalScriptSig(tokens) =>
case _ if ConditionalScriptSignature.isValidAsm(tokens) =>
ConditionalScriptSignature.fromAsm(tokens)
case _
if MultiSignatureScriptSignature.isMultiSignatureScriptSignature(
tokens) =>
case _ if MultiSignatureScriptSignature.isValidAsm(tokens) =>
MultiSignatureScriptSignature.fromAsm(tokens)
case _ if P2PKHScriptSignature.isP2PKHScriptSig(tokens) =>
case _ if P2PKHScriptSignature.isValidAsm(tokens) =>
P2PKHScriptSignature.fromAsm(tokens)
case _ if P2PKScriptSignature.isP2PKScriptSignature(tokens) =>
case _ if P2PKScriptSignature.isValidAsm(tokens) =>
P2PKScriptSignature.fromAsm(tokens)
case _ => NonStandardScriptSignature.fromAsm(tokens)
}
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = true
}

View File

@ -278,12 +278,11 @@ case class PSBT(
val out = tx.outputs(txIn.previousOutput.vout.toInt)
val outIsWitnessScript =
WitnessScriptPubKey.isWitnessScriptPubKey(out.scriptPubKey.asm)
WitnessScriptPubKey.isValidAsm(out.scriptPubKey.asm)
val hasWitScript = inputMap.witnessScriptOpt.isDefined
val hasWitRedeemScript =
inputMap.redeemScriptOpt.isDefined && WitnessScriptPubKey
.isWitnessScriptPubKey(
inputMap.redeemScriptOpt.get.redeemScript.asm)
.isValidAsm(inputMap.redeemScriptOpt.get.redeemScript.asm)
val notBIP143Vulnerable = {
!out.scriptPubKey.isInstanceOf[WitnessScriptPubKeyV0] && !(
hasWitRedeemScript &&
@ -317,7 +316,7 @@ case class PSBT(
* @return PSBT with added tx
*/
def addWitnessUTXOToInput(output: TransactionOutput, index: Int): PSBT = {
require(WitnessScriptPubKey.isWitnessScriptPubKey(output.scriptPubKey.asm),
require(WitnessScriptPubKey.isValidAsm(output.scriptPubKey.asm),
s"Given output was not a Witness UTXO: $output")
require(
index < inputMaps.size,
@ -351,10 +350,10 @@ case class PSBT(
val inputMap = inputMaps(index)
val isWitScript = WitnessScriptPubKey.isWitnessScriptPubKey(script.asm)
val isWitScript = WitnessScriptPubKey.isValidAsm(script.asm)
val redeemScriptOpt = inputMap.redeemScriptOpt
val hasRedeemScript = redeemScriptOpt.isDefined && WitnessScriptPubKey
.isWitnessScriptPubKey(redeemScriptOpt.get.redeemScript.asm)
.isValidAsm(redeemScriptOpt.get.redeemScript.asm)
val elements = if (!isWitScript && hasRedeemScript) {
inputMap.filterRecords(WitnessScriptKeyId) :+ InputPSBTRecord
@ -487,9 +486,9 @@ case class PSBT(
val outputMap = outputMaps(index)
val redeemScriptOpt = outputMap.redeemScriptOpt.map(_.redeemScript)
val isWitScript = WitnessScriptPubKey.isWitnessScriptPubKey(script.asm)
val isWitScript = WitnessScriptPubKey.isValidAsm(script.asm)
val hasWitScript = redeemScriptOpt.isDefined && WitnessScriptPubKey
.isWitnessScriptPubKey(redeemScriptOpt.get.asm)
.isValidAsm(redeemScriptOpt.get.asm)
val newElement =
if (!isWitScript && hasWitScript)

View File

@ -341,7 +341,6 @@ object P2PKWithTimeoutScriptPubKey
buildScript(
asm = asm.toVector,
constructor = P2PKWithTimeoutScriptPubKeyImpl.apply,
invariant = isP2PKWithTimeoutScriptPubKey,
errorMsg = s"Given asm was not a P2PKWithTimeoutScriptPubKey, got $asm"
)
}
@ -368,7 +367,7 @@ object P2PKWithTimeoutScriptPubKey
)
}
def isP2PKWithTimeoutScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
if (asm.length == 12) {
val pubKey = ECPublicKey.fromBytes(asm(2).bytes)
val lockTimeTry = Try(ScriptNumber.fromBytes(asm(5).bytes))
@ -393,7 +392,7 @@ We now need to ensure that `ScriptPubKey.fromAsm(p2pkWithTimeoutSPK.asm)` return
```scala mdoc:compile-only
asm match {
case Nil => EmptyScriptPubKey
case _ if P2PKWithTimeoutScriptPubKey.isP2PKWithTimeoutScriptPubKey(asm) =>
case _ if P2PKWithTimeoutScriptPubKey.isValidAsm(asm) =>
P2PKWithTimeoutScriptPubKey.fromAsm(asm)
//...
}
@ -430,7 +429,6 @@ object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptSignature = {
buildScript(asm.toVector,
P2PKScriptSignatureImpl(_),
isP2PKScriptSignature(_),
"The given asm tokens were not a p2pk script sig: " + asm)
}
@ -442,7 +440,7 @@ object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
}
/** P2PK scriptSigs always have the pattern [pushop, digitalSignature] */
def isP2PKScriptSignature(asm: Seq[ScriptToken]): Boolean = asm match {
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = asm match {
case Seq(_: BytesToPushOntoStack, _: ScriptConstant) => true
case _ => false
}
@ -464,7 +462,6 @@ object P2PKWithTimeoutScriptSignature
buildScript(
asm.toVector,
ConditionalScriptSignature.fromAsm,
isP2PKWithTimeoutScriptSignature,
s"The given asm tokens were not a P2PKWithTimeoutScriptSignature, got $asm"
)
}
@ -475,9 +472,9 @@ object P2PKWithTimeoutScriptSignature
ConditionalScriptSignature(P2PKScriptSignature(signature), beforeTimeout)
}
def isP2PKWithTimeoutScriptSignature(asm: Seq[ScriptToken]): Boolean = {
P2PKScriptSignature.isP2PKScriptSignature(asm.dropRight(1)) && ConditionalScriptSignature
.isValidConditionalScriptSig(asm)
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
P2PKScriptSignature.isValidAsm(asm.dropRight(1)) && ConditionalScriptSignature
.isValidAsm(asm)
}
}
```
@ -491,7 +488,7 @@ If you added a new `ScriptSignature` type in the previous step, you must add a `
```scala mdoc:compile-only
tokens match {
//...
case _ if P2PKScriptSignature.isP2PKScriptSignature(tokens) =>
case _ if P2PKScriptSignature.isValidAsm(tokens) =>
P2PKScriptSignature.fromAsm(tokens)
//...
}