mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 16:28:06 +00:00
210 lines
6.2 KiB
Python
210 lines
6.2 KiB
Python
"""
|
|
Pedersen hash implementation for StarkWare cryptography.
|
|
|
|
This module provides a full implementation of the Pedersen hash function
|
|
as specified by StarkWare, compatible with the reference implementation.
|
|
"""
|
|
|
|
from typing import List, Tuple, Union
|
|
|
|
# Handle both relative and absolute imports
|
|
try:
|
|
from .constants import (
|
|
FIELD_PRIME, ALPHA, BETA, N_ELEMENT_BITS_HASH,
|
|
SHIFT_POINT, CONSTANT_POINTS
|
|
)
|
|
except ImportError:
|
|
from constants import (
|
|
FIELD_PRIME, ALPHA, BETA, N_ELEMENT_BITS_HASH,
|
|
SHIFT_POINT, CONSTANT_POINTS
|
|
)
|
|
|
|
|
|
def _div_mod(n: int, m: int, p: int) -> int:
|
|
"""
|
|
Calculate (n / m) mod p.
|
|
|
|
Args:
|
|
n: The numerator
|
|
m: The denominator
|
|
p: The modulus
|
|
|
|
Returns:
|
|
int: The result of the division modulo p
|
|
"""
|
|
return (n * pow(m, -1, p)) % p
|
|
|
|
|
|
def _ec_add(p1: Tuple[int, int], p2: Tuple[int, int]) -> Tuple[int, int]:
|
|
"""
|
|
Add two points on the elliptic curve.
|
|
|
|
Args:
|
|
p1: The first point as (x, y) coordinates
|
|
p2: The second point as (x, y) coordinates
|
|
|
|
Returns:
|
|
Tuple[int, int]: The resulting point as (x, y) coordinates
|
|
"""
|
|
if p1[0] == p2[0]:
|
|
if (p1[1] + p2[1]) % FIELD_PRIME == 0:
|
|
# The points are negatives of each other, return the point at infinity
|
|
# We represent the point at infinity as None, but this should never happen
|
|
# in our use case, so we raise an exception instead
|
|
raise ValueError("Points are negatives of each other")
|
|
|
|
# The points are the same, so we're doubling
|
|
return _ec_double(p1)
|
|
|
|
# Calculate the slope
|
|
slope = _div_mod(p2[1] - p1[1], p2[0] - p1[0], FIELD_PRIME)
|
|
|
|
# Calculate the new point
|
|
x3 = (slope * slope - p1[0] - p2[0]) % FIELD_PRIME
|
|
y3 = (slope * (p1[0] - x3) - p1[1]) % FIELD_PRIME
|
|
|
|
return (x3, y3)
|
|
|
|
|
|
def _ec_double(p: Tuple[int, int]) -> Tuple[int, int]:
|
|
"""
|
|
Double a point on the elliptic curve.
|
|
|
|
Args:
|
|
p: The point to double as (x, y) coordinates
|
|
|
|
Returns:
|
|
Tuple[int, int]: The resulting point as (x, y) coordinates
|
|
"""
|
|
# Calculate the slope
|
|
slope = _div_mod(3 * p[0] * p[0] + ALPHA, 2 * p[1], FIELD_PRIME)
|
|
|
|
# Calculate the new point
|
|
x3 = (slope * slope - 2 * p[0]) % FIELD_PRIME
|
|
y3 = (slope * (p[0] - x3) - p[1]) % FIELD_PRIME
|
|
|
|
return (x3, y3)
|
|
|
|
|
|
def _ec_mult(m: int, p: Tuple[int, int]) -> Tuple[int, int]:
|
|
"""
|
|
Multiply a point on the elliptic curve by a scalar.
|
|
|
|
Args:
|
|
m: The scalar
|
|
p: The point as (x, y) coordinates
|
|
|
|
Returns:
|
|
Tuple[int, int]: The resulting point as (x, y) coordinates
|
|
"""
|
|
if m == 0:
|
|
raise ValueError("Cannot multiply by 0")
|
|
|
|
if m == 1:
|
|
return p
|
|
|
|
if m % 2 == 0:
|
|
return _ec_mult(m // 2, _ec_double(p))
|
|
else:
|
|
return _ec_add(p, _ec_mult(m - 1, p))
|
|
|
|
|
|
def pedersen_hash_as_point(*elements: int) -> Tuple[int, int]:
|
|
"""
|
|
Calculate the Pedersen hash of a list of integers and return the full EC point.
|
|
|
|
This is the full implementation following StarkWare's specification:
|
|
For each element, iterate through its 252 bits and add corresponding
|
|
constant points based on the bit values.
|
|
|
|
Args:
|
|
*elements: Variable number of integers to hash
|
|
|
|
Returns:
|
|
Tuple[int, int]: The resulting EC point as (x, y) coordinates
|
|
|
|
Raises:
|
|
ValueError: If any element is out of range or if there are insufficient constant points
|
|
"""
|
|
# Start with the shift point
|
|
point = tuple(SHIFT_POINT)
|
|
|
|
for i, element in enumerate(elements):
|
|
# Validate element is in valid range
|
|
if not (0 <= element < FIELD_PRIME):
|
|
raise ValueError(f"Element {element} is out of range [0, {FIELD_PRIME})")
|
|
|
|
# Calculate the starting index for this element's constant points
|
|
start_idx = 2 + i * N_ELEMENT_BITS_HASH
|
|
|
|
# Check if we have enough constant points
|
|
if start_idx + N_ELEMENT_BITS_HASH > len(CONSTANT_POINTS):
|
|
raise ValueError(f"Insufficient constant points for element {i}. Need {start_idx + N_ELEMENT_BITS_HASH}, have {len(CONSTANT_POINTS)}")
|
|
|
|
# Full implementation using all 252 bits
|
|
for j in range(N_ELEMENT_BITS_HASH):
|
|
pt = tuple(CONSTANT_POINTS[start_idx + j])
|
|
|
|
# Check for unhashable input (same x coordinate)
|
|
if point[0] == pt[0]:
|
|
raise ValueError('Unhashable input: point collision detected')
|
|
|
|
if element & 1:
|
|
point = _ec_add(point, pt)
|
|
element >>= 1
|
|
|
|
# Ensure all bits have been processed
|
|
if element != 0:
|
|
raise ValueError(f"Element too large: remaining bits {element}")
|
|
|
|
return point
|
|
|
|
|
|
def pedersen_hash(*elements: int) -> int:
|
|
"""
|
|
Calculate the Pedersen hash of a list of integers.
|
|
|
|
This function returns only the x-coordinate of the resulting EC point,
|
|
which is the standard Pedersen hash value.
|
|
|
|
Args:
|
|
*elements: Variable number of integers to hash
|
|
|
|
Returns:
|
|
int: The Pedersen hash as an integer (x-coordinate of the EC point)
|
|
|
|
Raises:
|
|
ValueError: If any element is out of range
|
|
"""
|
|
point = pedersen_hash_as_point(*elements)
|
|
return point[0]
|
|
|
|
|
|
def pedersen_hash_bytes(*elements: Union[int, bytes]) -> bytes:
|
|
"""
|
|
Calculate the Pedersen hash and return as bytes.
|
|
|
|
Args:
|
|
*elements: Variable number of integers or bytes to hash
|
|
|
|
Returns:
|
|
bytes: The hash result as 32 bytes (big-endian)
|
|
|
|
Raises:
|
|
ValueError: If any element is invalid
|
|
"""
|
|
# Convert bytes to integers if needed
|
|
int_elements = []
|
|
for element in elements:
|
|
if isinstance(element, bytes):
|
|
if len(element) > 32:
|
|
raise ValueError(f"Bytes element too long: {len(element)} > 32")
|
|
int_elements.append(int.from_bytes(element, byteorder='big'))
|
|
elif isinstance(element, int):
|
|
int_elements.append(element)
|
|
else:
|
|
raise ValueError(f"Invalid element type: {type(element)}")
|
|
|
|
hash_result = pedersen_hash(*int_elements)
|
|
return hash_result.to_bytes(32, byteorder='big')
|