2022 06 13 taprootspk xonlypubkey (#4388)

* Implement XOnlyPubKey inside of TaprootScriptPubKey

* Add ECPrivateKey.toXOnly

* Address code review
This commit is contained in:
Chris Stewart 2022-06-13 18:43:43 -05:00 committed by GitHub
parent 7e2ecd9d6a
commit ddbdde495d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 19 additions and 10 deletions

View File

@ -41,7 +41,7 @@ class ScriptPubKeyTest extends BitcoinSUnitTest {
}
it must "construct valid witness spk v1 for taproot" in {
val pubKey = CryptoGenerators.schnorrPublicKey.sample.get
val pubKey = CryptoGenerators.xOnlyPubKey.sample.get
val witSPKV1 = TaprootScriptPubKey.fromPubKey(pubKey)
assert(witSPKV1.pubKey == pubKey)
}

View File

@ -1347,7 +1347,7 @@ case class TaprootScriptPubKey(override val asm: Vector[ScriptToken])
override def witnessProgram: Seq[ScriptToken] = asm.tail.tail
override val scriptType: ScriptType = ScriptType.WITNESS_V1_TAPROOT
val pubKey: SchnorrPublicKey = SchnorrPublicKey.fromBytes(asm(2).bytes)
val pubKey: XOnlyPubKey = XOnlyPubKey.fromBytes(asm(2).bytes)
}
object TaprootScriptPubKey extends ScriptFactory[TaprootScriptPubKey] {
@ -1358,16 +1358,20 @@ object TaprootScriptPubKey extends ScriptFactory[TaprootScriptPubKey] {
s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
}
def apply(schnorrPubKey: SchnorrPublicKey): TaprootScriptPubKey = {
fromPubKey(schnorrPubKey)
def apply(xOnlyPubKey: XOnlyPubKey): TaprootScriptPubKey = {
fromPubKey(xOnlyPubKey)
}
def fromPubKey(schnorrPubKey: SchnorrPublicKey): TaprootScriptPubKey = {
val pushOp = BitcoinScriptUtil.calculatePushOp(schnorrPubKey.bytes)
val asm = OP_1 +: (pushOp ++ Vector(ScriptConstant(schnorrPubKey.bytes)))
def fromPubKey(xOnlyPubKey: XOnlyPubKey): TaprootScriptPubKey = {
val pushOp = BitcoinScriptUtil.calculatePushOp(xOnlyPubKey.bytes)
val asm = OP_1 +: (pushOp ++ Vector(ScriptConstant(xOnlyPubKey.bytes)))
fromAsm(asm)
}
def fromPubKey(schnorrPublicKey: SchnorrPublicKey): TaprootScriptPubKey = {
fromPubKey(schnorrPublicKey.toXOnly)
}
def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
val asmBytes = BytesUtil.toByteVector(asm)
asm.length == 3 &&
@ -1375,7 +1379,7 @@ object TaprootScriptPubKey extends ScriptFactory[TaprootScriptPubKey] {
WitnessScriptPubKey.isValidAsm(asm) &&
asmBytes.size == 34 &&
//have to make sure we have a valid xonly pubkey, not just 32 bytes
SchnorrPublicKey.fromBytesT(asm(2).bytes).isSuccess
XOnlyPubKey.fromBytesT(asm(2).bytes).isSuccess
}
}

View File

@ -238,6 +238,8 @@ case class ECPrivateKey(bytes: ByteVector)
SchnorrPublicKey(publicKey.bytes)
}
def toXOnly: XOnlyPubKey = schnorrPublicKey.toXOnly
def schnorrNonce: SchnorrNonce = {
SchnorrNonce(publicKey.bytes)
}

View File

@ -23,7 +23,8 @@ import org.bitcoins.crypto.{
Sha256Digest,
Sha256DigestBE,
Sha256Hash160Digest,
SipHashKey
SipHashKey,
XOnlyPubKey
}
import org.scalacheck.Gen
import scodec.bits.{BitVector, ByteVector}
@ -158,6 +159,8 @@ sealed abstract class CryptoGenerators {
def schnorrPublicKey: Gen[SchnorrPublicKey] =
publicKey.map(_.schnorrPublicKey)
def xOnlyPubKey: Gen[XOnlyPubKey] = publicKey.map(_.toXOnly)
/** Generate a sequence of private keys
* @param num maximum number of keys to generate
* @return

View File

@ -360,7 +360,7 @@ sealed abstract class ScriptGenerators {
def witnessScriptPubKeyV1: Gen[(TaprootScriptPubKey, Seq[ECPrivateKey])] = {
for {
priv <- CryptoGenerators.privateKey
pubKey = priv.schnorrPublicKey
pubKey = priv.toXOnly
} yield {
(TaprootScriptPubKey.fromPubKey(pubKey), Vector(priv))
}