core: Add carveout for parsing 'nonstandard' string to ScriptType.NONSTANDARD

This commit is contained in:
Chris Stewart 2025-02-17 09:06:27 -06:00
parent 9d84ff5d03
commit 33a14dee98
7 changed files with 18 additions and 28 deletions

View file

@ -11,6 +11,8 @@ class ScriptTypeTest extends BitcoinSUnitTest {
assert(newScriptType.contains(scriptType)) assert(newScriptType.contains(scriptType))
} }
assert(ScriptType.fromString("nonstandard") == ScriptType.NONSTANDARD)
} }
it must "fail when nonsense ScriptType is used" in { it must "fail when nonsense ScriptType is used" in {

View file

@ -550,14 +550,14 @@ object Int64
s"Cannot create ${super.getClass.getSimpleName} from $underlying") s"Cannot create ${super.getClass.getSimpleName} from $underlying")
} }
lazy val zero = checkCached(0) lazy val zero: Int64 = checkCached(0)
lazy val one = checkCached(1) lazy val one: Int64 = checkCached(1)
private lazy val minUnderlying: A = -9223372036854775808L private lazy val minUnderlying: A = -9223372036854775808L
private lazy val maxUnderlying: A = 9223372036854775807L private lazy val maxUnderlying: A = 9223372036854775807L
lazy val min = Int64(minUnderlying) lazy val min: Int64 = Int64(minUnderlying)
lazy val max = Int64(maxUnderlying) lazy val max: Int64 = Int64(maxUnderlying)
final override def fromNativeNumber(long: Long): Int64 = { final override def fromNativeNumber(long: Long): Int64 = {
Int64Impl(long) Int64Impl(long)

View file

@ -1486,8 +1486,8 @@ object TaprootScriptPubKey extends ScriptFactory[TaprootScriptPubKey] {
val asmBytes = BytesUtil.toByteVector(asm) val asmBytes = BytesUtil.toByteVector(asm)
asm.length == 3 && asm.length == 3 &&
asm.headOption.contains(OP_1) && asm.headOption.contains(OP_1) &&
WitnessScriptPubKey.isValidAsm(asm) &&
asmBytes.size == 34 && asmBytes.size == 34 &&
WitnessScriptPubKey.isValidAsm(asm) &&
// have to make sure we have a valid xonly pubkey, not just 32 bytes // have to make sure we have a valid xonly pubkey, not just 32 bytes
XOnlyPubKey.fromBytesT(asm(2).bytes).isSuccess XOnlyPubKey.fromBytesT(asm(2).bytes).isSuccess
} }

View file

@ -66,7 +66,12 @@ object ScriptType extends StringFactory[ScriptType] {
) )
override def fromStringOpt(string: String): Option[ScriptType] = override def fromStringOpt(string: String): Option[ScriptType] =
all.find(_.toString == string) all.find(_.toString.toLowerCase == string.toLowerCase) match {
case Some(x) => Some(x)
case None =>
if (string.toLowerCase == "nonstandard") Some(NONSTANDARD)
else None
}
/** Throws if given string is invalid */ /** Throws if given string is invalid */
override def fromString(string: String): ScriptType = override def fromString(string: String): ScriptType =

View file

@ -427,6 +427,8 @@ object ScriptConstant extends Factory[ScriptConstant] {
/** Represent a public key or hash of a public key on our stack. */ /** Represent a public key or hash of a public key on our stack. */
private case class ScriptConstantImpl(bytes: ByteVector) private case class ScriptConstantImpl(bytes: ByteVector)
extends ScriptConstant extends ScriptConstant {
override def toString: String = bytes.toHex
}
} }

View file

@ -167,7 +167,6 @@ sealed abstract class ScriptParser
} }
} }
loop(bytes).toVector loop(bytes).toVector
} }
/** Parses a redeem script from the given script token */ /** Parses a redeem script from the given script token */
@ -238,6 +237,7 @@ sealed abstract class ScriptParser
/** Parses OP_PUSHDATA operations correctly. Slices the appropriate amount of /** Parses OP_PUSHDATA operations correctly. Slices the appropriate amount of
* bytes off of the tail and pushes them onto the accumulator. * bytes off of the tail and pushes them onto the accumulator.
*
* @param op * @param op
* the script operation that is being parsed, this should be OP_PUSHDATA1, * the script operation that is being parsed, this should be OP_PUSHDATA1,
* OP_PUSHDATA2, OP_PUSHDATA4 or else it throws an exception * OP_PUSHDATA2, OP_PUSHDATA4 or else it throws an exception
@ -317,7 +317,6 @@ sealed abstract class ScriptParser
accum.++=(ArrayBuffer(op, bytesToPushOntoStack, scriptConstant)) accum.++=(ArrayBuffer(op, bytesToPushOntoStack, scriptConstant))
restOfBytes restOfBytes
} }
} }
/** Checks if a string can be cast to an int */ /** Checks if a string can be cast to an int */

View file

@ -5,7 +5,6 @@ import org.bitcoins.core.protocol.blockchain._
import org.bitcoins.crypto.CryptoUtil import org.bitcoins.crypto.CryptoUtil
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
/** Created by chris on 5/16/16. source of values: /** Created by chris on 5/16/16. source of values:
@ -43,24 +42,7 @@ sealed abstract class Base58 {
* [[org.bitcoins.core.protocol.blockchain.Base58Type Base58Type]] string. * [[org.bitcoins.core.protocol.blockchain.Base58Type Base58Type]] string.
*/ */
def encode(bytes: ByteVector): String = { def encode(bytes: ByteVector): String = {
val ones: String = bytes.toSeq.takeWhile(_ == 0).map(_ => '1').mkString bytes.toBase58
@tailrec
def loop(current: BigInt, str: String): String =
current match {
case _ if current == BigInt(0) =>
ones + str.reverse
case _: BigInt =>
val quotient: BigInt = current / BigInt(58L)
val remainder: BigInt = current.mod(58L)
val char = base58Characters.charAt(remainder.toInt).toString
val accum = str + char
loop(quotient, accum)
}
if (bytes.isEmpty) ""
else {
val big: BigInt = BigInt(1, bytes.toArray)
loop(big, "")
}
} }
/** Encodes a hex string to its /** Encodes a hex string to its