alloy_primitives/signature/
utils.rs

1use crate::ChainId;
2
3/// Applies [EIP-155](https://eips.ethereum.org/EIPS/eip-155).
4#[inline]
5pub const fn to_eip155_v(v: u8, chain_id: ChainId) -> ChainId {
6    (v as u64) + 35 + chain_id * 2
7}
8
9/// Normalizes a `v` value, respecting raw, legacy, and EIP-155 values.
10///
11/// This function covers the entire u64 range, producing v-values as follows:
12/// - 0-26 - raw/bare. 0-3 are legal. In order to ensure that all values are covered, we also handle
13///   4-26 here by returning v % 4.
14/// - 27-34 - legacy. 27-30 are legal. By legacy bitcoin convention range 27-30 signals uncompressed
15///   pubkeys, while 31-34 signals compressed pubkeys. We do not respect the compression convention.
16///   All Ethereum keys are uncompressed.
17/// - 35+ - EIP-155. By EIP-155 convention, `v = 35 + CHAIN_ID * 2 + 0/1` We return (v-1 % 2) here.
18///
19/// NB: raw and legacy support values 2, and 3, while EIP-155 does not.
20/// Recovery values of 2 and 3 are unlikely to occur in practice. In the
21/// vanishingly unlikely event  that you encounter an EIP-155 signature with a
22/// recovery value of 2 or 3, you should normalize out of band.
23#[cfg(feature = "k256")]
24#[inline]
25pub(crate) const fn normalize_v(v: u64) -> k256::ecdsa::RecoveryId {
26    let byte = normalize_v_to_byte(v);
27    debug_assert!(byte <= k256::ecdsa::RecoveryId::MAX);
28    match k256::ecdsa::RecoveryId::from_byte(byte) {
29        Some(recid) => recid,
30        None => unsafe { core::hint::unreachable_unchecked() },
31    }
32}
33
34/// Normalize the v value to a single byte.
35pub(crate) const fn normalize_v_to_byte(v: u64) -> u8 {
36    match v {
37        // Case 1: raw/bare
38        0..=26 => (v % 4) as u8,
39        // Case 2: non-EIP-155 v value
40        27..=34 => ((v - 27) % 4) as u8,
41        // Case 3: EIP-155 V value
42        35.. => ((v - 1) % 2) as u8,
43    }
44}
45
46#[cfg(test)]
47mod test {
48    #[test]
49    #[cfg(feature = "k256")]
50    fn normalizes_v() {
51        use super::*;
52        assert_eq!(normalize_v(27), k256::ecdsa::RecoveryId::from_byte(0).unwrap());
53        assert_eq!(normalize_v(28), k256::ecdsa::RecoveryId::from_byte(1).unwrap());
54    }
55}