mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-24 12:39:33 +00:00
7d2d739042
* Rename the coins folder to networks Ethereum isn't a coin. It's a network. Resolves #357. * More renames of coins -> networks in orchestration * Correct paths in tests/ * cargo fmt
44 lines
1.6 KiB
Solidity
44 lines
1.6 KiB
Solidity
// SPDX-License-Identifier: AGPLv3
|
|
pragma solidity ^0.8.0;
|
|
|
|
// see https://github.com/noot/schnorr-verify for implementation details
|
|
library Schnorr {
|
|
// secp256k1 group order
|
|
uint256 constant public Q =
|
|
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
|
|
|
// Fixed parity for the public keys used in this contract
|
|
// This avoids spending a word passing the parity in a similar style to
|
|
// Bitcoin's Taproot
|
|
uint8 constant public KEY_PARITY = 27;
|
|
|
|
error InvalidSOrA();
|
|
error MalformedSignature();
|
|
|
|
// px := public key x-coord, where the public key has a parity of KEY_PARITY
|
|
// message := 32-byte hash of the message
|
|
// c := schnorr signature challenge
|
|
// s := schnorr signature
|
|
function verify(
|
|
bytes32 px,
|
|
bytes memory message,
|
|
bytes32 c,
|
|
bytes32 s
|
|
) internal pure returns (bool) {
|
|
// ecrecover = (m, v, r, s) -> key
|
|
// We instead pass the following to obtain the nonce (not the key)
|
|
// Then we hash it and verify it matches the challenge
|
|
bytes32 sa = bytes32(Q - mulmod(uint256(s), uint256(px), Q));
|
|
bytes32 ca = bytes32(Q - mulmod(uint256(c), uint256(px), Q));
|
|
|
|
// For safety, we want each input to ecrecover to be 0 (sa, px, ca)
|
|
// The ecreover precomple checks `r` and `s` (`px` and `ca`) are non-zero
|
|
// That leaves us to check `sa` are non-zero
|
|
if (sa == 0) revert InvalidSOrA();
|
|
address R = ecrecover(sa, KEY_PARITY, px, ca);
|
|
if (R == address(0)) revert MalformedSignature();
|
|
|
|
// Check the signature is correct by rebuilding the challenge
|
|
return c == keccak256(abi.encodePacked(R, px, message));
|
|
}
|
|
}
|