ecRecover function
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);
}