core: Add Address descriptors from BIP385 (#5935)

This commit is contained in:
Chris Stewart 2025-02-18 10:35:10 -06:00 committed by GitHub
parent 62252daff8
commit 959993bed7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 199 additions and 96 deletions

View file

@ -386,6 +386,10 @@ class DescriptorTest extends BitcoinSUnitTest {
val str2 = "raw(a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87)"
val expected2 = "a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"
runTest(str2, expected2)
val str3 = "addr(3PUNyaW7M55oKWJ3kDukwk9bsKvryra15j)"
val expected3 = "a914eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee87"
runTest(str3, expected3)
}
it must "fail to parse invalid test vectors from BIP385" in {
@ -467,7 +471,7 @@ class DescriptorTest extends BitcoinSUnitTest {
}
def runTest(descriptor: String, expectedSPK: String): Assertion = {
val desc = ScriptDescriptor.fromString(descriptor)
val desc = Descriptor.fromString(descriptor)
assert(desc.toString == descriptor)
assert(desc.scriptPubKey.asmHex == expectedSPK)
}
@ -521,9 +525,9 @@ class DescriptorTest extends BitcoinSUnitTest {
val p2wpkh = P2WPKHWitnessSPKV0(derivedKey)
val spk = desc.expression.descriptorType match {
case DescriptorType.WPKH => p2wpkh
case DescriptorType.SH => P2SHScriptPubKey(p2wpkh)
case DescriptorType.TR =>
case ScriptDescriptorType.WPKH => p2wpkh
case ScriptDescriptorType.SH => P2SHScriptPubKey(p2wpkh)
case ScriptDescriptorType.TR =>
val scriptPathTreeExpr =
desc.expression.asInstanceOf[ScriptPathTreeExpression]
val internalExtKey = parseExtKeyExpression(scriptPathTreeExpr.keyPath)
@ -576,11 +580,11 @@ class DescriptorTest extends BitcoinSUnitTest {
MultiSignatureScriptPubKey(spk.requiredSigs, sortedKeys)
val (actual, expected) = {
desc.expression.descriptorType match {
case DescriptorType.Multi | DescriptorType.SortedMulti =>
case ScriptDescriptorType.Multi | ScriptDescriptorType.SortedMulti =>
(multisig, MultiSignatureScriptPubKey.fromAsmHex(s))
case DescriptorType.SH =>
case ScriptDescriptorType.SH =>
(P2SHScriptPubKey(multisig), P2SHScriptPubKey.fromAsmHex(s))
case DescriptorType.WSH =>
case ScriptDescriptorType.WSH =>
(P2WSHWitnessSPKV0(multisig), P2WSHWitnessSPKV0.fromAsmHex(s))
case x =>
sys.error(

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.protocol.script.descriptor.DescriptorType
import org.bitcoins.core.protocol.script.descriptor.ScriptDescriptorType
import org.bitcoins.core.script.ScriptType
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, *}
@ -51,7 +51,8 @@ sealed trait P2PKHScriptPubKey extends RawScriptPubKey {
def pubKeyHash: Sha256Hash160Digest =
Sha256Hash160Digest(asm(asm.length - 3).bytes)
override def toString = s"${DescriptorType.PK.toString}(${pubKeyHash.hex})"
override def toString =
s"${ScriptDescriptorType.PK.toString}(${pubKeyHash.hex})"
}
object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
@ -61,7 +62,8 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
extends P2PKHScriptPubKey {
override val scriptType: ScriptType = ScriptType.PUBKEYHASH
override def toString = s"${DescriptorType.PKH.toString}(${pubKeyHash.hex})"
override def toString =
s"${ScriptDescriptorType.PKH.toString}(${pubKeyHash.hex})"
}
def apply(pubKey: ECPublicKey): P2PKHScriptPubKey = {
@ -189,7 +191,7 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
}
override def toString =
s"${DescriptorType.Multi.toString}($requiredSigs,${publicKeys.mkString(",")})"
s"${ScriptDescriptorType.Multi.toString}($requiredSigs,${publicKeys.mkString(",")})"
}
object MultiSignatureScriptPubKey
@ -358,7 +360,8 @@ sealed trait P2SHScriptPubKey extends NonWitnessScriptPubKey {
def scriptHash: Sha256Hash160Digest =
Sha256Hash160Digest(asm(asm.length - 2).bytes)
override def toString = s"${DescriptorType.SH.toString}(${scriptHash.hex})"
override def toString =
s"${ScriptDescriptorType.SH.toString}(${scriptHash.hex})"
}
object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
@ -413,7 +416,8 @@ sealed trait P2PKScriptPubKey extends RawScriptPubKey {
def publicKey: ECPublicKeyBytes =
ECPublicKeyBytes(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
override def toString = s"${DescriptorType.PK.toString}(${publicKey.hex})"
override def toString =
s"${ScriptDescriptorType.PK.toString}(${publicKey.hex})"
}
@ -1343,7 +1347,8 @@ object WitnessScriptPubKeyV0 extends ScriptFactory[WitnessScriptPubKeyV0] {
*/
sealed abstract class P2WPKHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
def pubKeyHash: Sha256Hash160Digest = Sha256Hash160Digest(asm(2).bytes)
override def toString = s"${DescriptorType.WPKH.toString}(${pubKeyHash.hex})"
override def toString =
s"${ScriptDescriptorType.WPKH.toString}(${pubKeyHash.hex})"
}
object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
@ -1395,7 +1400,8 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
*/
sealed abstract class P2WSHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
def scriptHash: Sha256Digest = Sha256Digest(asm(2).bytes)
override def toString = s"${DescriptorType.WSH.toString}(${scriptHash.hex})"
override def toString =
s"${ScriptDescriptorType.WSH.toString}(${scriptHash.hex})"
}
object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
@ -1449,7 +1455,7 @@ case class TaprootScriptPubKey(override val asm: Vector[ScriptToken])
XOnlyPubKey.fromBytes(asm(2).bytes)
}
override def toString = s"${DescriptorType.TR.toString}(${pubKey.hex})"
override def toString = s"${ScriptDescriptorType.TR.toString}(${pubKey.hex})"
}
object TaprootScriptPubKey extends ScriptFactory[TaprootScriptPubKey] {

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.protocol.script.descriptor
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.number.{UInt64, UInt8}
import org.bitcoins.core.protocol.Bech32Address
import org.bitcoins.core.protocol.{Bech32Address, BitcoinAddress}
import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.util.Bech32
import org.bitcoins.crypto.{ECPrivateKey, PublicKey, StringFactory}
@ -16,7 +16,7 @@ sealed abstract class Descriptor {
def expression: DescriptorExpression
def checksum: Option[String]
def scriptPubKey: ScriptPubKey
override def toString: String = {
val checksumStr = checksum match {
case Some(c) => "#" + c
@ -128,6 +128,14 @@ case class ComboDescriptorCompressed(
val p2shp2wpkh: P2SHScriptPubKey = P2SHScriptPubKey(p2wpkh)
}
case class AddressDescriptor(
expression: AddressExpression,
checksum: Option[String])
extends Descriptor {
val address: BitcoinAddress = expression.address
override val scriptPubKey: ScriptPubKey = address.scriptPubKey
}
sealed abstract class DescriptorFactory[
T <: Descriptor,
E <: DescriptorExpression,
@ -167,9 +175,10 @@ sealed abstract class DescriptorFactory[
object RawDescriptor
extends DescriptorFactory[RawDescriptor,
RawScriptExpression,
DescriptorType.Raw.type] {
ScriptDescriptorType.Raw.type] {
override val descriptorType: DescriptorType.Raw.type = DescriptorType.Raw
override val descriptorType: ScriptDescriptorType.Raw.type =
ScriptDescriptorType.Raw
override protected def parseValidExpression(
iter: DescriptorIterator): RawScriptExpression = {
@ -187,8 +196,9 @@ object RawDescriptor
object P2WPKHDescriptor
extends DescriptorFactory[P2WPKHDescriptor,
P2WPKHExpression,
DescriptorType.WPKH.type] {
override val descriptorType: DescriptorType.WPKH.type = DescriptorType.WPKH
ScriptDescriptorType.WPKH.type] {
override val descriptorType: ScriptDescriptorType.WPKH.type =
ScriptDescriptorType.WPKH
override def parseValidExpression(
iter: DescriptorIterator): P2WPKHExpression = {
@ -226,8 +236,9 @@ object P2WPKHDescriptor
object P2WSHDescriptor
extends DescriptorFactory[P2WSHDescriptor,
P2WSHExpression,
DescriptorType.WSH.type] {
override val descriptorType: DescriptorType.WSH.type = DescriptorType.WSH
ScriptDescriptorType.WSH.type] {
override val descriptorType: ScriptDescriptorType.WSH.type =
ScriptDescriptorType.WSH
override protected def parseValidExpression(
iter: DescriptorIterator): P2WSHExpression = {
@ -245,8 +256,9 @@ object P2WSHDescriptor
object P2PKDescriptor
extends DescriptorFactory[P2PKDescriptor[PublicKey],
P2PKScriptExpression[PublicKey],
DescriptorType.PK.type] {
override val descriptorType: DescriptorType.PK.type = DescriptorType.PK
ScriptDescriptorType.PK.type] {
override val descriptorType: ScriptDescriptorType.PK.type =
ScriptDescriptorType.PK
override protected def parseValidExpression(
iter: DescriptorIterator): P2PKScriptExpression[PublicKey] = {
@ -264,8 +276,9 @@ object P2PKDescriptor
object P2PKHDescriptor
extends DescriptorFactory[P2PKHDescriptor,
P2PKHScriptExpression,
DescriptorType.PKH.type] {
override val descriptorType: DescriptorType.PKH.type = DescriptorType.PKH
ScriptDescriptorType.PKH.type] {
override val descriptorType: ScriptDescriptorType.PKH.type =
ScriptDescriptorType.PKH
override protected def parseValidExpression(
iter: DescriptorIterator): P2PKHScriptExpression = {
@ -283,8 +296,9 @@ object P2PKHDescriptor
object MultisigDescriptor
extends DescriptorFactory[MultisigDescriptor,
MultisigExpression,
DescriptorType.Multi.type] {
override val descriptorType: DescriptorType.Multi.type = DescriptorType.Multi
ScriptDescriptorType.Multi.type] {
override val descriptorType: ScriptDescriptorType.Multi.type =
ScriptDescriptorType.Multi
override protected def parseValidExpression(
iter: DescriptorIterator): MultisigExpression = {
@ -302,10 +316,10 @@ object MultisigDescriptor
object SortedMultisigDescriptor
extends DescriptorFactory[SortedMultisigDescriptor,
SortedMultisigExpression,
DescriptorType.SortedMulti.type] {
ScriptDescriptorType.SortedMulti.type] {
override val descriptorType: DescriptorType.SortedMulti.type =
DescriptorType.SortedMulti
override val descriptorType: ScriptDescriptorType.SortedMulti.type =
ScriptDescriptorType.SortedMulti
override protected def parseValidExpression(
iter: DescriptorIterator): SortedMultisigExpression = {
@ -323,8 +337,9 @@ object SortedMultisigDescriptor
object P2SHDescriptor
extends DescriptorFactory[P2SHDescriptor,
P2SHExpression,
DescriptorType.SH.type] {
override val descriptorType: DescriptorType.SH.type = DescriptorType.SH
ScriptDescriptorType.SH.type] {
override val descriptorType: ScriptDescriptorType.SH.type =
ScriptDescriptorType.SH
override protected def parseValidExpression(
iter: DescriptorIterator): P2SHExpression = {
@ -352,8 +367,9 @@ object P2SHDescriptor
object ComboDescriptor
extends DescriptorFactory[ComboDescriptor,
ComboExpression,
DescriptorType.Combo.type] {
override val descriptorType: DescriptorType.Combo.type = DescriptorType.Combo
ScriptDescriptorType.Combo.type] {
override val descriptorType: ScriptDescriptorType.Combo.type =
ScriptDescriptorType.Combo
override protected def parseValidExpression(
iter: DescriptorIterator): ComboExpression = {
@ -372,8 +388,9 @@ object ComboDescriptor
object TaprootDescriptor
extends DescriptorFactory[TaprootDescriptor,
TreeExpression,
DescriptorType.TR.type] {
override val descriptorType: DescriptorType.TR.type = DescriptorType.TR
ScriptDescriptorType.TR.type] {
override val descriptorType: ScriptDescriptorType.TR.type =
ScriptDescriptorType.TR
override protected def parseValidExpression(
iter: DescriptorIterator): TreeExpression = {
@ -395,22 +412,22 @@ object ScriptDescriptor extends StringFactory[ScriptDescriptor] {
? <: ScriptExpression,
? <: DescriptorType]] = {
Map(
DescriptorType.Raw -> RawDescriptor,
DescriptorType.WPKH -> P2WPKHDescriptor,
DescriptorType.WSH -> P2WSHDescriptor,
DescriptorType.PK -> P2PKDescriptor,
DescriptorType.SH -> P2SHDescriptor,
DescriptorType.PKH -> P2PKHDescriptor,
DescriptorType.Multi -> MultisigDescriptor,
DescriptorType.SortedMulti -> SortedMultisigDescriptor,
DescriptorType.Combo -> ComboDescriptor,
DescriptorType.TR -> TaprootDescriptor
ScriptDescriptorType.Raw -> RawDescriptor,
ScriptDescriptorType.WPKH -> P2WPKHDescriptor,
ScriptDescriptorType.WSH -> P2WSHDescriptor,
ScriptDescriptorType.PK -> P2PKDescriptor,
ScriptDescriptorType.SH -> P2SHDescriptor,
ScriptDescriptorType.PKH -> P2PKHDescriptor,
ScriptDescriptorType.Multi -> MultisigDescriptor,
ScriptDescriptorType.SortedMulti -> SortedMultisigDescriptor,
ScriptDescriptorType.Combo -> ComboDescriptor,
ScriptDescriptorType.TR -> TaprootDescriptor
)
}
override def fromString(string: String): ScriptDescriptor = {
val iter = DescriptorIterator(string)
val t = iter.takeDescriptorType()
val t = iter.takeScriptDescriptorType()
t match {
case s: ScriptDescriptorType =>
map
@ -422,6 +439,25 @@ object ScriptDescriptor extends StringFactory[ScriptDescriptor] {
}
}
object AddressDescriptor
extends DescriptorFactory[AddressDescriptor,
AddressExpression,
DescriptorType.Addr.type] {
override def descriptorType: DescriptorType.Addr.type = DescriptorType.Addr
override protected def parseValidExpression(
iter: DescriptorIterator): AddressExpression = {
iter.takeAddressExpression()
}
override protected def createDescriptor(
e: AddressExpression,
checksum: Option[String]): AddressDescriptor = {
AddressDescriptor(e, checksum)
}
}
object Descriptor extends StringFactory[Descriptor] {
final val charset: Vector[Char] = {
@ -440,6 +476,8 @@ object Descriptor extends StringFactory[Descriptor] {
t match {
case _: ScriptDescriptorType =>
ScriptDescriptor.fromString(string)
case DescriptorType.Addr =>
AddressDescriptor.fromString(string)
}
}

View file

@ -8,9 +8,10 @@ import org.bitcoins.core.crypto.{
ExtPublicKey
}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path, HardenedType}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.script.ScriptType
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
import scala.util.{Failure, Success}
@ -490,7 +491,7 @@ sealed abstract class MultisigScriptExpression
override def source: MultisigKeyExpression
def isSorted: Boolean = descriptorType == DescriptorType.SortedMulti
def isSorted: Boolean = descriptorType == ScriptDescriptorType.SortedMulti
}
@ -531,7 +532,8 @@ sealed trait TapscriptTreeExpressionSource extends ExpressionSource {
*/
case class RawScriptExpression(scriptPubKey: RawScriptPubKey)
extends RawSPKScriptExpression {
override val descriptorType: DescriptorType.Raw.type = DescriptorType.Raw
override val descriptorType: ScriptDescriptorType.Raw.type =
ScriptDescriptorType.Raw
override def toString: String = {
s"${descriptorType.toString}(${scriptPubKey.asmHex})"
@ -541,7 +543,8 @@ case class RawScriptExpression(scriptPubKey: RawScriptPubKey)
case class P2PKHScriptExpression(source: SingleECPublicKeyExpression)
extends RawSPKScriptExpression
with KeyExpressionScriptExpression[ECPublicKey] {
override val descriptorType: DescriptorType.PKH.type = DescriptorType.PKH
override val descriptorType: ScriptDescriptorType.PKH.type =
ScriptDescriptorType.PKH
override val scriptPubKey: P2PKHScriptPubKey = {
val pub = source.key match {
@ -555,7 +558,8 @@ case class P2PKHScriptExpression(source: SingleECPublicKeyExpression)
case class P2PKScriptExpression[T <: PublicKey](source: SingleKeyExpression[T])
extends RawSPKScriptExpression
with KeyExpressionScriptExpression[T] {
override val descriptorType: DescriptorType.PK.type = DescriptorType.PK
override val descriptorType: ScriptDescriptorType.PK.type =
ScriptDescriptorType.PK
override val scriptPubKey: P2PKScriptPubKey = {
val pub = source match {
@ -575,7 +579,8 @@ case class P2PKScriptExpression[T <: PublicKey](source: SingleKeyExpression[T])
case class P2WPKHExpression(source: SingleECPublicKeyExpression)
extends ScriptExpression
with KeyExpressionScriptExpression[ECPublicKey] {
override val descriptorType: DescriptorType.WPKH.type = DescriptorType.WPKH
override val descriptorType: ScriptDescriptorType.WPKH.type =
ScriptDescriptorType.WPKH
override val scriptPubKey: P2WPKHWitnessSPKV0 = {
val pubKey = source.key match {
@ -589,7 +594,8 @@ case class P2WPKHExpression(source: SingleECPublicKeyExpression)
case class P2WSHExpression(source: RawSPKScriptExpression)
extends ScriptExpression
with NestedScriptExpression {
override val descriptorType: DescriptorType.WSH.type = DescriptorType.WSH
override val descriptorType: ScriptDescriptorType.WSH.type =
ScriptDescriptorType.WSH
override val scriptPubKey: P2WSHWitnessSPKV0 = {
P2WSHWitnessSPKV0(source.scriptPubKey)
@ -599,7 +605,8 @@ case class P2WSHExpression(source: RawSPKScriptExpression)
case class P2SHExpression(source: ScriptExpression)
extends ScriptExpression
with NestedScriptExpression {
override val descriptorType: DescriptorType.SH.type = DescriptorType.SH
override val descriptorType: ScriptDescriptorType.SH.type =
ScriptDescriptorType.SH
override val scriptPubKey: P2SHScriptPubKey = {
source.scriptPubKey match {
@ -620,7 +627,8 @@ case class P2SHExpression(source: ScriptExpression)
case class MultisigExpression(source: MultisigKeyExpression)
extends MultisigScriptExpression
with KeyExpressionScriptExpression[ECPublicKey] {
override val descriptorType: DescriptorType.Multi.type = DescriptorType.Multi
override val descriptorType: ScriptDescriptorType.Multi.type =
ScriptDescriptorType.Multi
override val scriptPubKey: MultiSignatureScriptPubKey = {
MultiSignatureScriptPubKey(source.numSigsRequired, source.pubKeys)
@ -643,8 +651,8 @@ case class SortedMultisigExpression(source: MultisigKeyExpression)
extends MultisigScriptExpression
with KeyExpressionScriptExpression[ECPublicKey] {
override val descriptorType: DescriptorType.SortedMulti.type =
DescriptorType.SortedMulti
override val descriptorType: ScriptDescriptorType.SortedMulti.type =
ScriptDescriptorType.SortedMulti
override val scriptPubKey: MultiSignatureScriptPubKey = {
MultiSignatureScriptPubKey(source.numSigsRequired, source.sortedPubKeys)
@ -669,7 +677,7 @@ case class ComboExpression(
scriptType: ScriptType = ScriptType.PUBKEYHASH)
extends ScriptExpression
with KeyExpressionScriptExpression[ECPublicKey] {
override val descriptorType: DescriptorType = DescriptorType.Combo
override val descriptorType: DescriptorType = ScriptDescriptorType.Combo
override val scriptPubKey: ScriptPubKey = {
scriptType match {
@ -694,25 +702,27 @@ object ScriptExpressionECKey extends StringFactory[ScriptExpression] {
override def fromString(string: String): ScriptExpression = {
val iter = DescriptorIterator(string)
val descriptorType = iter.takeDescriptorType()
val descriptorType = iter.takeScriptDescriptorType()
val expression: ScriptExpression = descriptorType match {
case DescriptorType.PKH =>
case ScriptDescriptorType.PKH =>
P2PKHScriptExpression(iter.takeSingleECKeyExpression())
case DescriptorType.WPKH =>
case ScriptDescriptorType.WPKH =>
P2WPKHExpression(iter.takeSingleECKeyExpression())
case DescriptorType.WSH =>
case ScriptDescriptorType.WSH =>
P2WSHExpression(iter.takeRawSPKScriptExpression())
case DescriptorType.SH => P2SHExpression(iter.takeScriptExpressionECKey())
case DescriptorType.Raw => RawScriptExpression(iter.takeRawScriptPubKey())
case DescriptorType.PK =>
case ScriptDescriptorType.SH =>
P2SHExpression(iter.takeScriptExpressionECKey())
case ScriptDescriptorType.Raw =>
RawScriptExpression(iter.takeRawScriptPubKey())
case ScriptDescriptorType.PK =>
P2PKScriptExpression(iter.takeSingleECKeyExpression())
case DescriptorType.Multi =>
case ScriptDescriptorType.Multi =>
MultisigExpression(iter.takeMultisigKeyExpression())
case DescriptorType.SortedMulti =>
case ScriptDescriptorType.SortedMulti =>
SortedMultisigExpression(iter.takeMultisigKeyExpression())
case DescriptorType.Combo =>
case ScriptDescriptorType.Combo =>
ComboExpression(iter.takeSingleECKeyExpression())
case DescriptorType.TR =>
case ScriptDescriptorType.TR =>
sys.error(
s"Cannot create tapscript expression's with ECPublicKey, got=$string")
}
@ -724,17 +734,19 @@ object ScriptExpressionXOnlyKey extends StringFactory[ScriptExpression] {
override def fromString(string: String): ScriptExpression = {
val iter = DescriptorIterator(string)
val descriptorType = iter.takeDescriptorType()
val descriptorType = iter.takeScriptDescriptorType()
val expression: ScriptExpression = descriptorType match {
case DescriptorType.Raw => RawScriptExpression(iter.takeRawScriptPubKey())
case DescriptorType.PK =>
case ScriptDescriptorType.Raw =>
RawScriptExpression(iter.takeRawScriptPubKey())
case ScriptDescriptorType.PK =>
P2PKScriptExpression(iter.takeSingleXOnlyPubKeyExpression())
case DescriptorType.Multi =>
case ScriptDescriptorType.Multi =>
MultisigExpression(iter.takeMultisigKeyExpression())
case DescriptorType.SortedMulti =>
case ScriptDescriptorType.SortedMulti =>
SortedMultisigExpression(iter.takeMultisigKeyExpression())
case x @ (DescriptorType.Combo | DescriptorType.TR | DescriptorType.SH |
DescriptorType.WSH | DescriptorType.WPKH | DescriptorType.PKH) =>
case x @ (ScriptDescriptorType.Combo | ScriptDescriptorType.TR |
ScriptDescriptorType.SH | ScriptDescriptorType.WSH |
ScriptDescriptorType.WPKH | ScriptDescriptorType.PKH) =>
sys.error(
s"Cannot create tapscript descriptor for descriptorType=$x, got=$string")
}
@ -746,7 +758,8 @@ object ScriptExpressionXOnlyKey extends StringFactory[ScriptExpression] {
* https://github.com/bitcoin/bips/blob/master/bip-0386.mediawiki
*/
sealed abstract class TreeExpression extends ScriptExpression {
override def descriptorType: DescriptorType.TR.type = DescriptorType.TR
override def descriptorType: ScriptDescriptorType.TR.type =
ScriptDescriptorType.TR
override def scriptPubKey: TaprootScriptPubKey
}
@ -782,3 +795,10 @@ case class ScriptPathTreeExpression(
s"${descriptorType.toString}(${keyPath.source.toString},${source.toString()})"
}
}
case class AddressExpression(address: BitcoinAddress)
extends DescriptorExpression {
override def toString: String = {
s"${DescriptorType.Addr.toString}(${address.toString})"
}
}

View file

@ -2,11 +2,12 @@ package org.bitcoins.core.protocol.script.descriptor
import org.bitcoins.core.crypto.{ECPrivateKeyUtil, ExtKey, ExtPublicKey}
import org.bitcoins.core.hd.{BIP32Path, HardenedType}
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script.{
MultiSignatureScriptPubKey,
RawScriptPubKey
}
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
case class DescriptorIterator(descriptor: String) {
private var index: Int = 0
@ -20,6 +21,12 @@ case class DescriptorIterator(descriptor: String) {
()
}
def takeScriptDescriptorType(): ScriptDescriptorType = {
val t = ScriptDescriptorType.fromString(current)
skip(t.toString.length)
skip(1)
t
}
def takeDescriptorType(): DescriptorType = {
val t = DescriptorType.fromString(current)
skip(t.toString.length)
@ -247,4 +254,10 @@ case class DescriptorIterator(descriptor: String) {
}
expression
}
def takeAddressExpression(): AddressExpression = {
val addr = BitcoinAddress.fromString(current)
skip(addr.toString.length)
AddressExpression(addr)
}
}

View file

@ -12,8 +12,19 @@ sealed abstract class ScriptDescriptorType extends DescriptorType {
def scriptType: ScriptType
}
object DescriptorType extends StringFactory[DescriptorType] {
object ScriptDescriptorType extends StringFactory[ScriptDescriptorType] {
private[descriptor] val all: Vector[ScriptDescriptorType] = Vector(
PK,
PKH,
WPKH,
WSH,
TR,
Multi,
SortedMulti,
Raw,
SH,
Combo
)
case object PK extends ScriptDescriptorType {
override val scriptType: org.bitcoins.core.script.ScriptType.PUBKEY.type =
ScriptType.PUBKEY
@ -78,17 +89,28 @@ object DescriptorType extends StringFactory[DescriptorType] {
override val toString: String = "combo"
}
private val all: Vector[DescriptorType] = Vector(
PK,
PKH,
WPKH,
WSH,
TR,
Multi,
SortedMulti,
Raw,
SH,
Combo
override def fromStringOpt(string: String): Option[ScriptDescriptorType] = {
val (dType, _) = string.span(_ != '(')
all.find(d => dType == d.toString)
}
override def fromString(string: String): ScriptDescriptorType = {
fromStringOpt(string) match {
case Some(d) => d
case None =>
sys.error(s"Could not find descriptor type for string=$string")
}
}
}
object DescriptorType extends StringFactory[DescriptorType] {
case object Addr extends DescriptorType {
override val toString: String = "addr"
}
private val all: Vector[DescriptorType] = ScriptDescriptorType.all ++ Vector(
Addr
)
override def fromStringOpt(string: String): Option[DescriptorType] = {