2021 02 21 cheap redeemscript check (#2707)

* Add more cheap checks to see if a given script token is a redeemScript

* Make cheap witness version check before deserializing to a bytevector
This commit is contained in:
Chris Stewart 2021-02-23 10:37:40 -06:00 committed by GitHub
parent c6bf0bb1a3
commit 0d7edb7a68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 32 deletions

View file

@ -1176,17 +1176,23 @@ object WitnessScriptPubKey extends ScriptFactory[WitnessScriptPubKey] {
// ScriptConstantImpl(ByteVector(37 bytes, 0x0021020afd6012af90835558c68365a370b7e6cd1c0d4664a8656c8c7847185cb5db6651ae))) // ScriptConstantImpl(ByteVector(37 bytes, 0x0021020afd6012af90835558c68365a370b7e6cd1c0d4664a8656c8c7847185cb5db6651ae)))
//we can also have a LockTimeScriptPubKey with a nested 0 public key multisig script, need to check that as well //we can also have a LockTimeScriptPubKey with a nested 0 public key multisig script, need to check that as well
val bytes = BytesUtil.toByteVector(asm)
//it turns out this method gets called a lot when we attempt
//to type check spks, so let's do a cheap check before deserializing
//everything to a byte vector which is expensive
val firstOp = asm.headOption val firstOp = asm.headOption
if (bytes.size < 4 || bytes.size > 42) false if (!validWitVersions.contains(firstOp.getOrElse(OP_1NEGATE))) {
else if (!validWitVersions.contains(firstOp.getOrElse(OP_1NEGATE))) false false
} else {
val bytes = BytesUtil.toByteVector(asm)
if (bytes.length < 4 || bytes.length > 42) false
else if (MultiSignatureScriptPubKey.isValidAsm(asm)) false else if (MultiSignatureScriptPubKey.isValidAsm(asm)) false
else if (LockTimeScriptPubKey.isValidAsm(asm)) false else if (LockTimeScriptPubKey.isValidAsm(asm)) false
else if (asm(1).toLong + 2 == bytes.size) true else if (asm(1).toLong + 2 == bytes.size) true
else false else false
} }
} }
}
/** Represents a /** Represents a
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program BIP141 Witness program]] * [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program BIP141 Witness program]]

View file

@ -254,9 +254,18 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
/** Detects if the given script token is a redeem script */ /** Detects if the given script token is a redeem script */
def isRedeemScript(token: ScriptToken): Boolean = { def isRedeemScript(token: ScriptToken): Boolean = {
val redeemScript: ScriptPubKey = parseRedeemScript(token) token match {
case _: ScriptNumberOperation | _: ScriptOperation =>
//more cheap checks, we can't have a redeemScript
//if the token is OP_0/OP_1/OP_CHECKSIG etc
false
case constant: ScriptConstant =>
val redeemScript: ScriptPubKey = parseRedeemScript(constant)
isStandardNonP2SH(redeemScript, isRecursiveCall = false)
}
}
def isStandardNonP2SH( private def isStandardNonP2SH(
spk: ScriptPubKey, spk: ScriptPubKey,
isRecursiveCall: Boolean): Boolean = { isRecursiveCall: Boolean): Boolean = {
spk match { spk match {
@ -271,18 +280,17 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
conditional.secondSPK, conditional.secondSPK,
isRecursiveCall = true) isRecursiveCall = true)
case locktime: LockTimeScriptPubKey => case locktime: LockTimeScriptPubKey =>
Try(locktime.locktime).isSuccess && if (Try(locktime.locktime).isSuccess) {
isStandardNonP2SH(locktime.nestedScriptPubKey, isStandardNonP2SH(locktime.nestedScriptPubKey, isRecursiveCall = true)
isRecursiveCall = true) } else {
false
}
case _: NonStandardScriptPubKey | _: WitnessCommitment | case _: NonStandardScriptPubKey | _: WitnessCommitment |
_: P2SHScriptPubKey => _: P2SHScriptPubKey =>
false false
} }
} }
isStandardNonP2SH(redeemScript, isRecursiveCall = false)
}
/** Parses a redeem script from the given script token */ /** Parses a redeem script from the given script token */
def parseRedeemScript(scriptToken: ScriptToken): ScriptPubKey = { def parseRedeemScript(scriptToken: ScriptToken): ScriptPubKey = {
val asm = ScriptParser.fromBytes(scriptToken.bytes) val asm = ScriptParser.fromBytes(scriptToken.bytes)