ecRecover function

PublicKey ecRecover(
  1. Curve curve,
  2. EthSignature sig,
  3. List<int> hash
)

Implementation

PublicKey ecRecover(Curve curve, EthSignature sig, List<int> hash) {
  var r = sig.R;
  var s = sig.S;
  var v = sig.getV();

  if (r <= BigInt.zero || r >= curve.n || s <= BigInt.zero || s >= curve.n) {
    throw Exception('Invalid signature');
  }

  // Ethereum uses v values of 27 or 28
  if (v < 27 || v > 34) {
    throw ArgumentError('Invalid recovery id (v). It must be 27 <= v <= 34.');
  }


  // Adjust v for elliptic library (0 or 1)
  var recId = v - 27;
  var isYOdd = recId & 1 != 0; // Oddness of Y coordinate
  var isSecondKey = recId >> 1 != 0; // 1 means the second key candidate

  // Calculate the public key point from r, s, and the hash
  var n = curve.n; // curve order
  var x = r + (isSecondKey ? n : BigInt.zero); // Compute x

  // Try to recover y coordinate
  var ySquared = (x.modPow(BigInt.from(3), curve.p) + curve.a * x + curve.b) % curve.p;
  var y = ySquared.modPow((curve.p + BigInt.one) ~/ BigInt.from(4), curve.p);

  if (y.isOdd != (v % 2 == 1)) {
    y = curve.p - y;
  }

  var R = _decompressPoint(curve, x, isYOdd);

  if (!curve.isOnCurve(R)) {
    throw Exception('Invalid curve point');
  }

  var rInv = r.modInverse(n);

  var pubKeyPoint = curve.scalarMul(R, intToOctets(s, s.bitLength));
  var added = curve.scalarMul(curve.G, hash);
  pubKeyPoint = curve.add(pubKeyPoint, AffinePoint.fromXY(added.X, -added.Y));
  pubKeyPoint = curve.scalarMul(pubKeyPoint, intToOctets(rInv, rInv.bitLength));

  return PublicKey(curve, pubKeyPoint.X, pubKeyPoint.Y);
}