Introduction
In blockchain and Web3 technologies, secure communication and identity verification are made possible by digital signatures. On Ethereum, one of the core tools for verifying these signatures is the built-in Solidity function ecrecover
.
In this post, we’ll explore what ecrecover
is, why we need the v
, r
, and s
parameters, what they represent, and how digital signature verification works. We'll also walk through practical examples of both signing and verification.
Digital Signatures and Ethereum's Signing Process
Ethereum uses the ECDSA (Elliptic Curve Digital Signature Algorithm) over the secp256k1 curve to ensure the security of messages and transactions. Here's how it works:
-
A user signs the hash of a message with their private key.
-
The result is a digital signature composed of three parts:
-
r
: A 32-byte value generated during the signing process. -
s
: Another 32-byte value. -
v
: A 1-byte recovery identifier used to determine the correct public key from the signature. In Ethereum, this is typically27
or28
.
-
Together, these three values prove that the message was signed by someone in possession of the corresponding private key.
The ecrecover Function and Its Parameters
In Solidity, the ecrecover
function is used as follows:
address signer = ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s);
-
hash
: Thekeccak256
hash of the signed message. -
v
,r
, ands
: The three components of the digital signature.
This function returns the Ethereum address that created the signature, allowing you to verify the authenticity of the signer directly onchain.
Technical Breakdown of v
, r
, and s
-
r: First 32-byte value output from the ECDSA signing process.
-
s: Second 32-byte value of the signature.
-
v: Recovery parameter, helps choose the correct public key. Usually 27 or 28 in Ethereum.
The v
value is necessary because the same r
and s
can produce two possible public keys. v
tells us which one is valid.
Practical Example: Signing a Message in TypeScript
import { ethers } from "ethers";
async function signMessage() {
const wallet = ethers.Wallet.createRandom();
const message = "Hello Ethereum!";
const signature = await wallet.signMessage(message);
const sig = ethers.Signature.from(signature);
console.log("Address:", wallet.address);
console.log("Message:", message);
console.log("v:", sig.v);
console.log("r:", sig.r);
console.log("s:", sig.s);
}
signMessage();
This script creates a random wallet, signs a message, and logs the v
, r
, and s
values to the console.
Practical Example: Verifying a Signature in Solidity
pragma solidity ^0.8.0;
contract VerifySignature {
function verify(
bytes32 messageHash,
uint8 v,
bytes32 r,
bytes32 s,
address expectedSigner
) public pure returns (bool) {
return ecrecover(messageHash, v, r, s) == expectedSigner;
}
}
This contract takes the messageHash
and signature components, recovers the signer’s address, and checks if it matches the expected address.
Ecrecover in Zero-Knowledge Circuits
Using ecrecover
directly inside zero-knowledge proof (ZKP) circuits is computationally expensive. Instead, most ZK circuits accept the signer’s public key (split into x
and y
coordinates) as an input.
Here’s an example in Noir:
fn main(
pub_key_x: [u8; 32],
pub_key_y: [u8; 32],
signature: [u8; 64],
hashed_message: pub [u8; 32]
) -> pub Field {
let address = ecrecover::ecrecover(pub_key_x, pub_key_y, signature, hashed_message);
address
}
This approach avoids the cost of recovering the public key from the signature and makes the circuit more efficient.
Post-EIP-155: How v
Changes
EIP-155 introduced a modification to the v
parameter to include the chainId
as a protection against replay attacks.
The formula is:
v = CHAIN_ID * 2 + 35 or CHAIN_ID * 2 + 36
Examples:
-
On Ethereum Mainnet (
chainId = 1
), possiblev
values are37
and38
. -
On Polygon (
chainId = 137
), possible values are309
and310
.
This ensures that the same signature cannot be reused across multiple chains.
Security Notes & Tips
-
While
v
is typically27
or28
, EIP-155 allows it to take other values. -
Always validate the
v
value correctly during signature verification. -
Always check that the address returned by
ecrecover
matches the expected signer. -
To prevent replay attacks, include a
nonce
orchainId
in the message being signed.
Conclusion
Ethereum’s ecrecover
function is a foundational tool for verifying digital signatures on-chain. Understanding how it works including the role of v
, r
, and s
is critical for writing secure smart contracts.
As zero-knowledge technologies evolve, verifying signatures efficiently off-chain and inside ZK circuits becomes more practical but at the core, it all starts with understanding how signature recovery works.
评论 (0)