diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorTest.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorTest.scala index d0a6e75dc8..218e8cf7d5 100644 --- a/core-test/src/test/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorTest.scala @@ -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( diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 26d18f671c..5119a60454 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -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] { diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/Descriptor.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/Descriptor.scala index e6aa1e0ee0..8f443b36b9 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/Descriptor.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/Descriptor.scala @@ -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) } } diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorExpression.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorExpression.scala index 4a29a6056a..9104e1d36c 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorExpression.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorExpression.scala @@ -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})" + } +} diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorIterator.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorIterator.scala index b32fc61a70..197b976089 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorIterator.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorIterator.scala @@ -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) + } } diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorType.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorType.scala index b2ce4d8f52..14d093ab5c 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorType.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/descriptor/DescriptorType.scala @@ -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] = {