verifyBip340SignatureUsingXOnly static method

bool verifyBip340SignatureUsingXOnly({
  1. required List<int> xOnly,
  2. required List<int> digest,
  3. required List<int> signature,
  4. List<int>? tapTweakHash,
})

Verifies a BIP-340 Schnorr signature using an x-only public key.

This method checks whether the given Schnorr signature is valid for the provided message digest and x-only public key. It optionally applies a tweak if tapTweakHash is provided.

  • 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 CryptoSignException(
        "The digest must be a ${BitcoinSignerUtils.baselen}-byte array.");
  }
  if (xOnly.length != EcdsaKeysConst.pointCoordByteLen) {
    throw CryptoSignException("Invalid xOnly 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",
    List<int>.from([...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;
}