signSchnorrTransaction method

List<int> signSchnorrTransaction(
  1. List<int> digest, {
  2. required List tapScripts,
  3. required bool tweak,
})

Signs a given Schnorr-based transaction digest using the Schnorr signature scheme.

Implementation

List<int> signSchnorrTransaction(List<int> digest,
    {required List<dynamic> tapScripts, required bool tweak}) {
  if (digest.length != 32) {
    throw const ArgumentException("The message must be a 32-byte array.");
  }
  List<int> byteKey = <int>[];
  if (tweak) {
    final t = P2TRUtils.calculateTweek(verifyKey.verifyKey.publicKey.point,
        script: tapScripts);

    byteKey = BitcoinSignerUtils.calculatePrivateTweek(
        signingKey.privateKey.toBytes(), BigintUtils.fromBytes(t));
  } else {
    byteKey = signingKey.privateKey.toBytes();
  }
  final List<int> aux = QuickCrypto.sha256Hash(<int>[...digest, ...byteKey]);
  final d0 = BigintUtils.fromBytes(byteKey);

  if (!(BigInt.one <= d0 && d0 <= BitcoinSignerUtils._order - BigInt.one)) {
    throw const ArgumentException(
        "The secret key must be an integer in the range 1..n-1.");
  }
  final P = Curves.generatorSecp256k1 * d0;
  BigInt d = d0;
  if (P.y.isOdd) {
    d = BitcoinSignerUtils._order - d;
  }

  final t = BytesUtils.xor(
      BigintUtils.toBytes(d, length: BitcoinSignerUtils.baselen),
      P2TRUtils.taggedHash("BIP0340/aux", aux));

  final kHash = P2TRUtils.taggedHash(
    "BIP0340/nonce",
    <int>[
      ...t,
      ...BigintUtils.toBytes(P.x, length: BitcoinSignerUtils.baselen),
      ...digest
    ],
  );
  final k0 = BigintUtils.fromBytes(kHash) % BitcoinSignerUtils._order;

  if (k0 == BigInt.zero) {
    throw const MessageException(
        'Failure. This happens only with negligible probability.');
  }
  final R = (Curves.generatorSecp256k1 * k0);
  BigInt k = k0;
  if (R.y.isOdd) {
    k = BitcoinSignerUtils._order - k;
  }

  final eHash = P2TRUtils.taggedHash(
    "BIP0340/challenge",
    List<int>.from([
      ...BigintUtils.toBytes(R.x, length: BitcoinSignerUtils.baselen),
      ...BigintUtils.toBytes(P.x, length: BitcoinSignerUtils.baselen),
      ...digest
    ]),
  );

  final e = BigintUtils.fromBytes(eHash) % BitcoinSignerUtils._order;

  final eKey = (k + e * d) % BitcoinSignerUtils._order;
  final sig = List<int>.from([
    ...BigintUtils.toBytes(R.x, length: BitcoinSignerUtils.baselen),
    ...BigintUtils.toBytes(eKey, length: BitcoinSignerUtils.baselen)
  ]);
  final verify = verifyKey.verifySchnorr(digest, sig,
      tapleafScripts: tapScripts, isTweak: tweak);

  if (!verify) {
    throw const MessageException(
        'The created signature does not pass verification.');
  }
  return sig;
}