verifyBip340SignatureUsingXOnly static method
Verifies a BIP-340 Schnorr signature using an x-only public key.
xOnly: A 32-byte x-only public key (corresponding to the Taproot key).digest: A 32-byte message digest that was signed.signature: A 64-byte Schnorr signature (R.x || s).tapTweakHash(optional): A 32-byte tweak hash used to modify the public key.
Implementation
static bool verifyBip340SignatureUsingXOnly({
required List<int> xOnly,
required List<int> digest,
required List<int> signature,
List<int>? tapTweakHash,
}) {
if (digest.length != BitcoinSignerUtils.baselen) {
throw ArgumentException.invalidOperationArguments(
"verifyBip340SignatureUsingXOnly",
name: "digest",
reason: "Invalid digest bytes length.",
);
}
if (xOnly.length != EcdsaKeysConst.pointCoordByteLen) {
throw ArgumentException.invalidOperationArguments(
"verifyBip340SignatureUsingXOnly",
name: "xOnly",
reason: "Invalid xOnlyPublicKey bytes length.",
);
}
final schnorrSignature = BitcoinSchnorrSignature.fromBytes(signature);
final x = BigintUtils.fromBytes(xOnly);
final P =
tapTweakHash != null
? tweakKey(xBig: x, tapTweakHash: tapTweakHash)
: P2TRUtils.liftX(x);
final ProjectiveECCPoint generator = BitcoinSignerUtils.generator;
final BigInt prime = BitcoinSignerUtils.generator.curve.p;
if (schnorrSignature.r >= prime ||
schnorrSignature.s >= BitcoinSignerUtils.order) {
return false;
}
final eHash = P2TRUtils.taggedHash("BIP0340/challenge", [
...schnorrSignature.rBytes(),
...P.toXonly(),
...digest,
]);
BigInt e = BigintUtils.fromBytes(eHash) % BitcoinSignerUtils.order;
final sp = generator * schnorrSignature.s;
if (P.y.isEven) {
e = BitcoinSignerUtils.order - e;
}
final ProjectiveECCPoint eP = P * e;
final R = sp + eP;
if (R.y.isOdd || R.x != schnorrSignature.r) {
return false;
}
return true;
}