core: Add ADT for LeafVersion

This commit is contained in:
Chris Stewart 2025-03-06 15:29:27 -06:00
parent aeca55f5ef
commit b407141146
10 changed files with 69 additions and 53 deletions

View file

@ -1,9 +1,9 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.Bech32mAddress
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.script.*
import org.bitcoins.crypto.{Sha256Digest, XOnlyPubKey}
import upickle.default._
import upickle.default.*
case class Given(internalPubkey: XOnlyPubKey, treeOpt: Option[TapscriptTree]) {
@ -74,7 +74,7 @@ object TaprootWalletTestCase {
else if (`given`.objOpt.isDefined) {
val givenObj = `given`.obj
val script = ScriptPubKey.fromAsmHex(givenObj("script").str)
val leafVersion = givenObj("leafVersion").num.toByte
val leafVersion = LeafVersion.fromByte(givenObj("leafVersion").num.toByte)
val leaf = TapLeaf(leafVersion, script)
Some(leaf)
} else {

View file

@ -50,7 +50,7 @@ class TaprootWitnessTest extends BitcoinSUnitTest {
"7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac"
val spk = ScriptPubKey.fromAsmHex(asmHex)
assert(asmHex == spk.asmHex)
val leaf = TapLeaf(0xc0.toByte, spk)
val leaf = TapLeaf(LeafVersion.Tapscript, spk)
val hash = TaprootScriptPath.computeTapleafHash(leaf)
assert(hash.hex == expected)
}

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.protocol.script.descriptor
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.script.*
import org.bitcoins.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.scalatest.Assertion
@ -536,7 +536,7 @@ class DescriptorTest extends BitcoinSUnitTest {
case pub: ECPublicKey => pub.toXOnly
}
val tree =
TapLeaf(TapLeaf.leafVersion, P2PKScriptPubKey(derivedKey.toXOnly))
TapLeaf(LeafVersion.Tapscript, P2PKScriptPubKey(derivedKey.toXOnly))
val (_, spk) =
TaprootScriptPubKey.fromInternalKeyTapscriptTree(internal, tree)
spk

View file

@ -22,11 +22,11 @@ sealed abstract class ControlBlock extends NetworkElement {
XOnlyPubKey.fromBytes(bytes.slice(1, 33))
}
val leafVersion: Byte =
(bytes.head & TaprootScriptPath.TAPROOT_LEAF_MASK).toByte
val leafVersion: LeafVersion =
LeafVersion.fromByte((bytes.head & LeafVersion.TAPROOT_LEAF_MASK).toByte)
val isTapLeafMask: Boolean = {
(bytes.head & TaprootScriptPath.TAPROOT_LEAF_MASK).toByte == TaprootScriptPath.TAPROOT_LEAF_TAPSCRIPT
(bytes.head & LeafVersion.TAPROOT_LEAF_MASK).toByte == LeafVersion.Tapscript.toByte
}
/** Leaf or branch hashes embedded in the control block */
@ -71,7 +71,8 @@ object TapscriptControlBlock extends Factory[TapscriptControlBlock] {
if (bytes.isEmpty) {
false
} else {
TapLeaf.knownLeafVersions.contains(bytes.head) &&
LeafVersion.knownLeafVersions.contains(
LeafVersion.fromMaskedByte(bytes.head)) &&
ControlBlock.isValid(bytes) &&
XOnlyPubKey.fromBytesT(bytes.slice(1, 33)).isSuccess
}
@ -79,7 +80,7 @@ object TapscriptControlBlock extends Factory[TapscriptControlBlock] {
/** Creates a control block with no scripts, just an internal key */
def fromXOnlyPubKey(internalKey: XOnlyPubKey): TapscriptControlBlock = {
fromBytes(TapLeaf.leafVersion +: internalKey.bytes)
fromBytes(LeafVersion.Tapscript.toByte +: internalKey.bytes)
}
override def fromBytes(bytes: ByteVector): TapscriptControlBlock = {

View file

@ -0,0 +1,35 @@
package org.bitcoins.core.protocol.script
sealed abstract class LeafVersion {
def toByte: Byte
}
object LeafVersion {
/** BIP342 specifies validity rules that apply for leaf version 0xc0, but
* future proposals can introduce rules for other leaf versions.
*
* @see
* https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#rationale
*/
case object Tapscript extends LeafVersion {
override def toByte: Byte = 0xc0.toByte
}
case class UnknownLeafVersion(toByte: Byte) extends LeafVersion
val knownLeafVersions: Vector[LeafVersion] = Vector(
Tapscript /*, 0xc1.toByte*/ )
final val TAPROOT_LEAF_MASK: Byte = 0xfe.toByte
def fromByte(byte: Byte): LeafVersion = {
knownLeafVersions
.find(_.toByte == byte)
.getOrElse(UnknownLeafVersion(byte))
}
def fromMaskedByte(byte: Byte): LeafVersion = {
fromByte((TAPROOT_LEAF_MASK & byte).toByte)
}
}

View file

@ -7,7 +7,7 @@ import org.bitcoins.core.serializers.script.{
ScriptParser
}
import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
import scodec.bits.ByteVector
/** Created by chris on 11/10/16. The witness used to evaluate a
@ -416,10 +416,6 @@ object TaprootScriptPath extends Factory[TaprootScriptPath] {
final val TAPROOT_CONTROL_MAX_SIZE: Int = {
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT
}
final val TAPROOT_LEAF_MASK: Byte = 0xfe.toByte
final val TAPROOT_LEAF_TAPSCRIPT: Byte = 0xc0.toByte
override def fromBytes(bytes: ByteVector): TaprootScriptPath = {
RawScriptWitnessParser.read(bytes) match {
case t: TaprootScriptPath => t
@ -485,7 +481,8 @@ object TaprootScriptPath extends Factory[TaprootScriptPath] {
*/
def computeTapleafHash(leaf: TapLeaf): Sha256Digest = {
val bytes =
ByteVector.fromInt(i = leaf.leafVersion, size = 1) ++ leaf.spk.bytes
ByteVector.fromInt(i = leaf.leafVersion.toByte,
size = 1) ++ leaf.spk.bytes
CryptoUtil.tapLeafHash(bytes)
}

View file

@ -29,10 +29,11 @@ case class TapBranch(tree1: TapscriptTree, tree2: TapscriptTree)
}
}
case class TapLeaf(leafVersion: Byte, spk: ScriptPubKey) extends TapscriptTree {
case class TapLeaf(leafVersion: LeafVersion, spk: ScriptPubKey)
extends TapscriptTree {
override val bytes: ByteVector =
ByteVector.fromInt(leafVersion, 1) ++ spk.bytes
ByteVector.fromInt(leafVersion.toByte, 1) ++ spk.bytes
val sha256: Sha256Digest = CryptoUtil.tapLeafHash(bytes)
override val leafs: Vector[TapLeaf] = Vector(this)
@ -41,18 +42,6 @@ case class TapLeaf(leafVersion: Byte, spk: ScriptPubKey) extends TapscriptTree {
}
}
object TapLeaf {
val leafVersion: Byte = 0xc0.toByte
/** BIP342 specifies validity rules that apply for leaf version 0xc0, but
* future proposals can introduce rules for other leaf versions.
*
* @see
* https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#rationale
*/
val knownLeafVersions: Vector[Byte] = Vector(leafVersion, 0xc1.toByte)
}
object TapscriptTree {
def buildTapscriptTree(leafs: Vector[TapLeaf]): TapscriptTree = {

View file

@ -386,7 +386,7 @@ case class TapscriptLeafExpression(source: RawSPKScriptExpression)
private def scriptPubKey: ScriptPubKey = source.scriptPubKey
override def tree: TapLeaf =
TapLeaf(TapLeaf.leafVersion, scriptPubKey)
TapLeaf(LeafVersion.Tapscript, scriptPubKey)
}
object SingleECPublicKeyExpression

View file

@ -4,7 +4,7 @@ import org.bitcoins.core.crypto.ExtPublicKey
import org.bitcoins.core.hd.BIP32Path
import org.bitcoins.core.number.{Int32, UInt32, UInt64}
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
NonWitnessTransaction,
@ -13,7 +13,7 @@ import org.bitcoins.core.protocol.transaction.{
}
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{HashType, _}
import org.bitcoins.crypto.*
import scodec.bits.ByteVector
import scala.annotation.tailrec
@ -59,7 +59,7 @@ sealed trait GlobalPSBTRecord extends PSBTRecord {
}
object GlobalPSBTRecord extends Factory[GlobalPSBTRecord] {
import org.bitcoins.core.psbt.PSBTGlobalKeyId._
import org.bitcoins.core.psbt.PSBTGlobalKeyId.*
case class UnsignedTransaction(transaction: NonWitnessTransaction)
extends GlobalPSBTRecord {
@ -107,7 +107,7 @@ object GlobalPSBTRecord extends Factory[GlobalPSBTRecord] {
}
override def fromBytes(bytes: ByteVector): GlobalPSBTRecord = {
import org.bitcoins.core.psbt.PSBTGlobalKeyId._
import org.bitcoins.core.psbt.PSBTGlobalKeyId.*
val (key, value) = PSBTRecord.fromBytes(bytes)
PSBTGlobalKeyId.fromByte(key.head) match {
@ -141,7 +141,7 @@ sealed trait InputPSBTRecord extends PSBTRecord {
}
object InputPSBTRecord extends Factory[InputPSBTRecord] {
import org.bitcoins.core.psbt.PSBTInputKeyId._
import org.bitcoins.core.psbt.PSBTInputKeyId.*
case class NonWitnessOrUnknownUTXO(transactionSpent: Transaction)
extends InputPSBTRecord {
@ -352,7 +352,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
case class TRLeafScript(
controlBlock: ControlBlock,
script: RawScriptPubKey,
leafVersion: Byte)
leafVersion: LeafVersion)
extends InputPSBTRecord {
override type KeyId = TRLeafScriptKeyId.type
@ -361,7 +361,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
}
override val value: ByteVector = {
script.asmBytes ++ ByteVector.fromByte(leafVersion)
script.asmBytes ++ ByteVector.fromByte(leafVersion.toByte)
}
}
@ -411,7 +411,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
}
override def fromBytes(bytes: ByteVector): InputPSBTRecord = {
import org.bitcoins.core.psbt.PSBTInputKeyId._
import org.bitcoins.core.psbt.PSBTInputKeyId.*
val (key, value) = PSBTRecord.fromBytes(bytes)
PSBTInputKeyId.fromByte(key.head) match {
@ -530,7 +530,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
val script = RawScriptPubKey.fromAsmBytes(value.init)
TRLeafScript(controlBlock, script, value.last)
TRLeafScript(controlBlock, script, LeafVersion.fromByte(value.last))
case TRBIP32DerivationPathKeyId =>
val pubKey = XOnlyPubKey(key.tail)
val numHashes = CompactSizeUInt.fromBytes(value)
@ -575,7 +575,7 @@ sealed trait OutputPSBTRecord extends PSBTRecord {
}
object OutputPSBTRecord extends Factory[OutputPSBTRecord] {
import org.bitcoins.core.psbt.PSBTOutputKeyId._
import org.bitcoins.core.psbt.PSBTOutputKeyId.*
case class RedeemScript(redeemScript: ScriptPubKey) extends OutputPSBTRecord {
override type KeyId = RedeemScriptKeyId.type

View file

@ -1,18 +1,12 @@
package org.bitcoins.core.script
import org.bitcoins.core.crypto._
import org.bitcoins.core.crypto.*
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{
TapLeaf,
TaprootKeyPath,
TaprootScriptPath,
TaprootUnknownPath,
TaprootWitness
}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.script.constant.*
import org.bitcoins.core.script.control.{OP_ELSE, OP_ENDIF, OP_IF, OP_NOTIF}
import org.bitcoins.core.script.flag.ScriptFlag
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.result.*
import org.bitcoins.core.util.BitcoinScriptUtil
import org.bitcoins.crypto.Sha256Digest
@ -91,7 +85,7 @@ sealed trait ScriptProgram {
*/
def tapLeafHashOpt: Option[Sha256Digest] = {
getTapscriptOpt.map { sp =>
val leaf = TapLeaf(TaprootScriptPath.TAPROOT_LEAF_TAPSCRIPT, sp.script)
val leaf = TapLeaf(LeafVersion.Tapscript, sp.script)
val hash = TaprootScriptPath.computeTapleafHash(leaf)
hash
}