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/// Attempts to normalize the v value to a boolean parity value.
10///
11/// Returns `None` if the value is invalid for any of the known Ethereum parity encodings.
12#[inline]
13pub const fn normalize_v(v: u64) -> Option<bool> {
14    if !is_valid_v(v) {
15        return None;
16    }
17
18    // Simplifying:
19    //  0| 1 => v % 2 == 0
20    // 27|28 => (v - 27) % 2 == 0
21    //  35.. => (v - 35) % 2 == 0
22    // ---
23    //  0| 1 => v % 2 == 0
24    // 27|28 => v % 2 == 1
25    //  35.. => v % 2 == 1
26    // ---
27    //   ..2 => v % 2 == 0
28    //     _ => v % 2 == 1
29    let cmp = (v <= 1) as u64;
30    Some(v % 2 == cmp)
31}
32
33/// Returns `true` if the given `v` value is valid for any of the known Ethereum parity encodings.
34#[inline]
35const fn is_valid_v(v: u64) -> bool {
36    matches!(
37        v,
38        // Case 1: raw/bare
39        0 | 1
40        // Case 2: non-EIP-155 v value
41        | 27 | 28
42        // Case 3: EIP-155 V value
43        | 35..
44    )
45}
46
47#[cfg(test)]
48mod test {
49    use super::*;
50
51    #[test]
52    fn normalizes_v() {
53        assert_eq!(normalize_v(0), Some(false));
54        assert_eq!(normalize_v(1), Some(true));
55
56        for invalid_v in 2..27 {
57            assert_eq!(normalize_v(invalid_v), None);
58        }
59
60        assert_eq!(normalize_v(27), Some(false));
61        assert_eq!(normalize_v(28), Some(true));
62
63        for invalid_v in 29..35 {
64            assert_eq!(normalize_v(invalid_v), None);
65        }
66
67        assert_eq!(normalize_v(35), Some(false));
68        assert_eq!(normalize_v(36), Some(true));
69        for v in 35..100 {
70            assert_eq!(normalize_v(v), Some((v - 35) % 2 != 0));
71        }
72    }
73}