Skip to content

Instantly share code, notes, and snippets.

@msinkec
Created April 21, 2025 17:26
Show Gist options
  • Save msinkec/f6df0729f3d34e39e64d92143a174f47 to your computer and use it in GitHub Desktop.
Save msinkec/f6df0729f3d34e39e64d92143a174f47 to your computer and use it in GitHub Desktop.
Cheap k*G in EVM
// SPDX‑License‑Identifier: MIT
pragma solidity ^0.8.25;
/// Check that (Qx,Qy) = k·G on secp256k1
///
/// The trick is to use EVM precompile 0x01 (`ecrecover`) as a cheap
/// “k · G” oracle:
/// • Pass z = 0 (message hash)
/// • Pass r = Gx (x‑coordinate of the generator G)
/// • Pass v = 27 (because Gy is even)
/// • Pass s = k (the scalar we want multiplied)
///
/// Internally ecrecover reconstructs the public key P = k·G,
/// then returns `keccak256(P)[12:]` as an Ethereum address.
///
/// We recompute the same address client‑side from the caller‑supplied
/// point (Qx,Qy). If the two match, the point is valid.
library Secp256k1Verify {
uint256 private constant GX =
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798;
uint8 private constant V = 27; // Gy is even
/// @dev returns true iff (Qx,Qy) = k·G
function verifyPoint(
uint256 k, // scalar to test
uint256 Qx, // x‑coordinate of claimed point
uint256 Qy // y‑coordinate of claimed point
) internal view returns (bool ok) {
// 1. Oracle: address derived from k·G via ecrecover
address oracle = ecrecover(
bytes32(0), // z = 0
V, // parity of Gy
bytes32(GX), // r = Gx
bytes32(k) // s = k
);
// 2. Address derived from caller‑supplied (Qx,Qy)
address claimed = address(
uint160(uint256(keccak256(abi.encodePacked(Qx, Qy))))
);
// 3. Equal ⇒ the point is correct
ok = (oracle == claimed);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment