Skip to content

Commit

Permalink
perf: improve normalize_v (#792)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Oct 30, 2024
1 parent b81e678 commit 6e962a8
Showing 1 changed file with 57 additions and 12 deletions.
69 changes: 57 additions & 12 deletions crates/primitives/src/signature/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,41 @@ pub const fn to_eip155_v(v: u8, chain_id: ChainId) -> ChainId {
(v as u64) + 35 + chain_id * 2
}

/// Attempts to normalize the v value to a boolean parity value. Returns None if the value is
/// invalid for any of the known Ethereum parity encodings.
/// Attempts to normalize the v value to a boolean parity value.
///
/// Returns `None` if the value is invalid for any of the known Ethereum parity encodings.
pub const fn normalize_v(v: u64) -> Option<bool> {
match v {
if !is_valid_v(v) {
return None;
}

// Simplifying:
// 0| 1 => v % 2 == 0
// 27|28 => (v - 27) % 2 == 0
// 35.. => (v - 35) % 2 == 0
// ---
// 0| 1 => v % 2 == 0
// 27|28 => v % 2 == 1
// 35.. => v % 2 == 1
// ---
// ..2 => v % 2 == 0
// _ => v % 2 == 1
let cmp = (v <= 1) as u64;
Some(v % 2 == cmp)
}

/// Returns `true` if the given `v` value is valid for any of the known Ethereum parity encodings.
#[inline]
const fn is_valid_v(v: u64) -> bool {
matches!(
v,
// Case 1: raw/bare
0 => Some(false),
1 => Some(true),
0 | 1
// Case 2: non-EIP-155 v value
27 => Some(false),
28 => Some(true),
| 27 | 28
// Case 3: EIP-155 V value
35.. => Some(((v - 35) % 2) != 0),
_ => None,
}
| 35..
)
}

/// Normalizes a `v` value, respecting raw, legacy, and EIP-155 values.
Expand Down Expand Up @@ -61,10 +82,34 @@ pub(crate) const fn normalize_v_to_byte(v: u64) -> u8 {

#[cfg(test)]
mod test {
use super::*;

#[test]
#[cfg(feature = "k256")]
fn normalizes_v() {
use super::*;
assert_eq!(normalize_v(0), Some(false));
assert_eq!(normalize_v(1), Some(true));

for invalid_v in 2..27 {
assert_eq!(normalize_v(invalid_v), None);
}

assert_eq!(normalize_v(27), Some(false));
assert_eq!(normalize_v(28), Some(true));

for invalid_v in 29..35 {
assert_eq!(normalize_v(invalid_v), None);
}

assert_eq!(normalize_v(35), Some(false));
assert_eq!(normalize_v(36), Some(true));
for v in 35..100 {
assert_eq!(normalize_v(v), Some((v - 35) % 2 != 0));
}
}

#[test]
#[cfg(feature = "k256")]
fn normalizes_v_to_recid() {
assert_eq!(normalize_v_to_recid(27), k256::ecdsa::RecoveryId::from_byte(0).unwrap());
assert_eq!(normalize_v_to_recid(28), k256::ecdsa::RecoveryId::from_byte(1).unwrap());
}
Expand Down

0 comments on commit 6e962a8

Please sign in to comment.