mirror of
https://github.com/logos-co/nomos-specs.git
synced 2025-02-16 01:07:31 +00:00
109 lines
2.9 KiB
Python
109 lines
2.9 KiB
Python
from keum import grumpkin, PrimeFiniteField
|
|
import poseidon
|
|
|
|
|
|
# !Important! The crypto primitives here must be in agreement with the proving system
|
|
# E.g. if you are using noir with the Barretenberg, we must use the Grumpkin curve.
|
|
|
|
Point = grumpkin.AffineWeierstrass
|
|
Field = grumpkin.Fq
|
|
|
|
|
|
class Field(PrimeFiniteField):
|
|
ORDER = poseidon.prime_64
|
|
|
|
|
|
def poseidon_grumpkin_field():
|
|
# TODO: These parameters are made up.
|
|
# return poseidon.Poseidon(
|
|
# p=Field.ORDER,
|
|
# security_level=128,
|
|
# alpha=5,
|
|
# input_rate=3,
|
|
# t=9,
|
|
# )
|
|
h, _ = poseidon.case_simple()
|
|
# h, _ = poseidon.case_neptune()
|
|
# h = poseidon.Poseidon(
|
|
# p=Field.ORDER,
|
|
# security_level=128,
|
|
# alpha=5,
|
|
# input_rate=3,
|
|
# t=9,
|
|
# )
|
|
|
|
# TODO: this is hacks on hacks to make poseidon take in arbitrary input length.
|
|
# Fix is to implement a sponge as described in section 2.1 of
|
|
# https://eprint.iacr.org/2019/458.pdf
|
|
def inner(data):
|
|
assert all(
|
|
isinstance(d, Field) for d in data
|
|
), f"{data}\n{[type(d) for d in data]}"
|
|
data = [d.v for d in data]
|
|
digest = 0
|
|
for i in range(0, len(data), h.input_rate - 1):
|
|
digest = h.run_hash([digest, *data[i : i + h.input_rate - 1]])
|
|
return digest
|
|
|
|
return inner
|
|
|
|
|
|
POSEIDON = poseidon_grumpkin_field()
|
|
|
|
|
|
def prf(domain, *elements) -> Field:
|
|
return Field(int(POSEIDON([*_str_to_vec(domain), *elements])))
|
|
|
|
|
|
def hash_to_curve(domain, *elements) -> Point:
|
|
# HACK: we don't currently have a proper hash_to_curve implementation
|
|
# so we hack the Point.random() function.
|
|
#
|
|
# Point.random() calls into the global `random` module to generate a
|
|
# point. We will seed the random module with the result of hashing the
|
|
# elements and then call Point.random() to retreive the point
|
|
# corresponding to the mentioned elements.
|
|
|
|
r = prf(f"HASH_TO_CURVE_{domain}", *elements)
|
|
|
|
import random
|
|
|
|
random.seed(r.v)
|
|
return Point.random()
|
|
|
|
|
|
def comm(*elements):
|
|
"""
|
|
Returns a commitment to the sequence of elements.
|
|
|
|
The commitmtent can be opened at index 0..len(elements)
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
|
|
def pederson_commit(value: Field, blinding: Field, domain: Point) -> Point:
|
|
return Point.generator().mul(value) + domain.mul(blinding)
|
|
|
|
|
|
def merkle_root(data) -> Field:
|
|
data = _pad_to_power_of_2(data)
|
|
nodes = [CRH(d) for d in data]
|
|
while len(nodes) > 1:
|
|
nodes = [CRH(nodes[i], nodes[i + 1]) for i in range(0, len(nodes), 2)]
|
|
|
|
return nodes[0]
|
|
|
|
|
|
def _pad_to_power_of_2(data):
|
|
import math
|
|
|
|
max_lower_bound = int(math.log2(len(data)))
|
|
if 2**max_lower_bound == len(data):
|
|
return data
|
|
to_pad = 2 ** (max_lower_bound + 1) - len(data)
|
|
return data + [Field.zero()] * to_pad
|
|
|
|
|
|
def _str_to_vec(s):
|
|
return [Field(ord(c)) for c in s]
|