secp256k1ECmultGen static method

void secp256k1ECmultGen(
  1. Secp256k1ECmultGenContext ctx,
  2. Secp256k1Gej r,
  3. Secp256k1Scalar gn
)

Implementation

static void secp256k1ECmultGen(
    Secp256k1ECmultGenContext ctx, Secp256k1Gej r, Secp256k1Scalar gn) {
  int combOff;
  Secp256k1Ge add = Secp256k1Ge();
  Secp256k1Fe neg = Secp256k1Fe();
  Secp256k1GeStorage adds = Secp256k1GeStorage();
  Secp256k1Scalar d = Secp256k1Scalar();
  List<int> recoded = List.filled((Secp256k1Const.combBits + 31) >> 5, 0);
  int first = 1, i;

  /// Compute the scalar d = (gn + ctx->scalarOffset).
  secp256k1ScalarAdd(d, ctx.scalarOffset, gn);

  /// Convert to recoded array.
  for (i = 0; i < 8 && i < (Secp256k1Const.combBits + 31) >> 5; ++i) {
    recoded[i] = secp256k1ScalarGetBitsLimb32(d, 32 * i, 32);
  }

  /// Outer loop: iterate over combOff from combSpacing - 1 down to 0.
  const int combSpacing = 1;
  const int combPoints = 32;
  combOff = combSpacing - 1;
  const int combBlocks = 43;
  const int combTeeth = 6;
  List<List<Secp256k1GeStorage>> secp256k1ECmultGenPrecTable = tables
      .map((e) => e
          .map((e) => Secp256k1GeStorage.constants(
              BigInt.from(e[0]),
              BigInt.from(e[1]),
              BigInt.from(e[2]),
              BigInt.from(e[3]),
              BigInt.from(e[4]),
              BigInt.from(e[5]),
              BigInt.from(e[6]),
              BigInt.from(e[7]),
              BigInt.from(e[8]),
              BigInt.from(e[9]),
              BigInt.from(e[10]),
              BigInt.from(e[11]),
              BigInt.from(e[12]),
              BigInt.from(e[13]),
              BigInt.from(e[14]),
              BigInt.from(e[15])))
          .toList())
      .toList();
  while (true) {
    int block;
    int bitPos = combOff;

    /// Inner loop: for each block, add table entries to the result.
    for (block = 0; block < combBlocks; ++block) {
      int bits = 0, sign, abs, index, tooth;
      for (tooth = 0; tooth < combTeeth; ++tooth) {
        int bitdata = secp256k1Rotr32(recoded[bitPos >> 5], bitPos & 0x1f);

        /// Clear the bit at position tooth, but sssh, don't tell clang.
        int vmask = (~(1 << tooth)).toUnsigned(32);
        bits = (bits & vmask).toUnsigned(32);

        /// Write the bit into position tooth (and junk into higher bits).
        bits = (bits ^ bitdata << tooth).toUnsigned(32);
        bitPos = (bitPos + combSpacing).toUnsigned(32);
      }

      sign = ((bits >> (combTeeth - 1)) & 1).toUnsigned(32);
      abs = (bits ^ -sign) & (combPoints - 1);
      _cond(sign == 0 || sign == 1, "secp256k1ECmultGen");

      _cond(abs < combPoints, "secp256k1ECmultGen");

      for (index = 0; index < combPoints; ++index) {
        secp256k1GeStorageCmov(adds,
            secp256k1ECmultGenPrecTable[block][index], (index == abs).toInt);
      }

      /// Set add=adds or add=-adds, in constant time, based on sign.
      secp256k1GeFromStorage(add, adds);
      secp256k1FeNegate(neg, add.y, 1);
      secp256k1FeCmov(add.y, neg, sign);

      /// Add the looked up and conditionally negated value to r.
      if (first != 0) {
        /// If this is the first table lookup, we can skip addition.
        secp256k1GejSetGe(r, add);

        /// Give the entry a random Z coordinate to blind intermediary results.
        secp256k1GejRescale(r, ctx.projBlind);
        first = 0;
      } else {
        secp256k1GejAddGe(r, r, add);
      }
    }

    /// Double the result, except in the last iteration.
    if (combOff-- == 0) break;
    secp256k1GejDouble(r, r);
  }

  secp256k1GejAddGe(r, r, ctx.geOffset);
}