From 468561eb0a9f4d8b4c5297d12706d7712b9920e2 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Fri, 30 Jul 2021 15:58:12 +0100 Subject: [PATCH] wip --- verkle_trie_pedersen/_blst.so | Bin 0 -> 306448 bytes verkle_trie_pedersen/blst.py | 237 +++++++++ verkle_trie_pedersen/compute_stats.sh | 15 + verkle_trie_pedersen/ipa.py | 114 +++++ verkle_trie_pedersen/pippenger.py | 68 +++ verkle_trie_pedersen/poly_utils.py | 298 ++++++++++++ verkle_trie_pedersen/verkle_trie.py | 677 ++++++++++++++++++++++++++ 7 files changed, 1409 insertions(+) create mode 100755 verkle_trie_pedersen/_blst.so create mode 100644 verkle_trie_pedersen/blst.py create mode 100755 verkle_trie_pedersen/compute_stats.sh create mode 100644 verkle_trie_pedersen/ipa.py create mode 100644 verkle_trie_pedersen/pippenger.py create mode 100644 verkle_trie_pedersen/poly_utils.py create mode 100644 verkle_trie_pedersen/verkle_trie.py diff --git a/verkle_trie_pedersen/_blst.so b/verkle_trie_pedersen/_blst.so new file mode 100755 index 0000000000000000000000000000000000000000..3bc7193c520b6ffe9cf6d17a3e5692c880645c82 GIT binary patch literal 306448 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 XO;;Iz*HTdYH`hq~Lwyl^AFclfToqRs literal 0 HcmV?d00001 diff --git a/verkle_trie_pedersen/blst.py b/verkle_trie_pedersen/blst.py new file mode 100644 index 00000000..0dd6f080 --- /dev/null +++ b/verkle_trie_pedersen/blst.py @@ -0,0 +1,237 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 4.0.1 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + +from sys import version_info as _swig_python_version_info +if _swig_python_version_info < (2, 7, 0): + raise RuntimeError("Python 2.7 or later required") + +# Import the low-level C/C++ module +if __package__ or "." in __name__: + from . import _blst +else: + import _blst + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +_swig_new_instance_method = _blst.SWIG_PyInstanceMethod_New +_swig_new_static_method = _blst.SWIG_PyStaticMethod_New + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + + +def _swig_setattr_nondynamic_instance_variable(set): + def set_instance_attr(self, name, value): + if name == "thisown": + self.this.own(value) + elif name == "this": + set(self, name, value) + elif hasattr(self, name) and isinstance(getattr(type(self), name), property): + set(self, name, value) + else: + raise AttributeError("You cannot add instance attributes to %s" % self) + return set_instance_attr + + +def _swig_setattr_nondynamic_class_variable(set): + def set_class_attr(cls, name, value): + if hasattr(cls, name) and not isinstance(getattr(cls, name), property): + set(cls, name, value) + else: + raise AttributeError("You cannot add class attributes to %s" % cls) + return set_class_attr + + +def _swig_add_metaclass(metaclass): + """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" + def wrapper(cls): + return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) + return wrapper + + +class _SwigNonDynamicMeta(type): + """Meta class to enforce nondynamic attributes (no new attributes) for a class""" + __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) + + +BLST_SUCCESS = _blst.BLST_SUCCESS +BLST_BAD_ENCODING = _blst.BLST_BAD_ENCODING +BLST_POINT_NOT_ON_CURVE = _blst.BLST_POINT_NOT_ON_CURVE +BLST_POINT_NOT_IN_GROUP = _blst.BLST_POINT_NOT_IN_GROUP +BLST_AGGR_TYPE_MISMATCH = _blst.BLST_AGGR_TYPE_MISMATCH +BLST_VERIFY_FAIL = _blst.BLST_VERIFY_FAIL +BLST_PK_IS_INFINITY = _blst.BLST_PK_IS_INFINITY +class SecretKey(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + keygen = _swig_new_instance_method(_blst.SecretKey_keygen) + from_bendian = _swig_new_instance_method(_blst.SecretKey_from_bendian) + from_lendian = _swig_new_instance_method(_blst.SecretKey_from_lendian) + to_bendian = _swig_new_instance_method(_blst.SecretKey_to_bendian) + to_lendian = _swig_new_instance_method(_blst.SecretKey_to_lendian) + + def __init__(self): + _blst.SecretKey_swiginit(self, _blst.new_SecretKey()) + __swig_destroy__ = _blst.delete_SecretKey + +# Register SecretKey in _blst: +_blst.SecretKey_swigregister(SecretKey) + +class P1_Affine(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.P1_Affine_swiginit(self, _blst.new_P1_Affine(*args)) + dup = _swig_new_instance_method(_blst.P1_Affine_dup) + to_jacobian = _swig_new_instance_method(_blst.P1_Affine_to_jacobian) + serialize = _swig_new_instance_method(_blst.P1_Affine_serialize) + compress = _swig_new_instance_method(_blst.P1_Affine_compress) + on_curve = _swig_new_instance_method(_blst.P1_Affine_on_curve) + in_group = _swig_new_instance_method(_blst.P1_Affine_in_group) + is_inf = _swig_new_instance_method(_blst.P1_Affine_is_inf) + is_equal = _swig_new_instance_method(_blst.P1_Affine_is_equal) + core_verify = _swig_new_instance_method(_blst.P1_Affine_core_verify) + generator = _swig_new_static_method(_blst.P1_Affine_generator) + __swig_destroy__ = _blst.delete_P1_Affine + +# Register P1_Affine in _blst: +_blst.P1_Affine_swigregister(P1_Affine) +P1_Affine_generator = _blst.P1_Affine_generator + +class P1(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.P1_swiginit(self, _blst.new_P1(*args)) + dup = _swig_new_instance_method(_blst.P1_dup) + to_affine = _swig_new_instance_method(_blst.P1_to_affine) + serialize = _swig_new_instance_method(_blst.P1_serialize) + compress = _swig_new_instance_method(_blst.P1_compress) + on_curve = _swig_new_instance_method(_blst.P1_on_curve) + in_group = _swig_new_instance_method(_blst.P1_in_group) + is_inf = _swig_new_instance_method(_blst.P1_is_inf) + is_equal = _swig_new_instance_method(_blst.P1_is_equal) + aggregate = _swig_new_instance_method(_blst.P1_aggregate) + sign_with = _swig_new_instance_method(_blst.P1_sign_with) + hash_to = _swig_new_instance_method(_blst.P1_hash_to) + encode_to = _swig_new_instance_method(_blst.P1_encode_to) + mult = _swig_new_instance_method(_blst.P1_mult) + cneg = _swig_new_instance_method(_blst.P1_cneg) + neg = _swig_new_instance_method(_blst.P1_neg) + add = _swig_new_instance_method(_blst.P1_add) + dbl = _swig_new_instance_method(_blst.P1_dbl) + generator = _swig_new_static_method(_blst.P1_generator) + __swig_destroy__ = _blst.delete_P1 + +# Register P1 in _blst: +_blst.P1_swigregister(P1) +P1_generator = _blst.P1_generator + +class P2_Affine(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.P2_Affine_swiginit(self, _blst.new_P2_Affine(*args)) + dup = _swig_new_instance_method(_blst.P2_Affine_dup) + to_jacobian = _swig_new_instance_method(_blst.P2_Affine_to_jacobian) + serialize = _swig_new_instance_method(_blst.P2_Affine_serialize) + compress = _swig_new_instance_method(_blst.P2_Affine_compress) + on_curve = _swig_new_instance_method(_blst.P2_Affine_on_curve) + in_group = _swig_new_instance_method(_blst.P2_Affine_in_group) + is_inf = _swig_new_instance_method(_blst.P2_Affine_is_inf) + is_equal = _swig_new_instance_method(_blst.P2_Affine_is_equal) + core_verify = _swig_new_instance_method(_blst.P2_Affine_core_verify) + generator = _swig_new_static_method(_blst.P2_Affine_generator) + __swig_destroy__ = _blst.delete_P2_Affine + +# Register P2_Affine in _blst: +_blst.P2_Affine_swigregister(P2_Affine) +P2_Affine_generator = _blst.P2_Affine_generator + +class P2(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.P2_swiginit(self, _blst.new_P2(*args)) + dup = _swig_new_instance_method(_blst.P2_dup) + to_affine = _swig_new_instance_method(_blst.P2_to_affine) + serialize = _swig_new_instance_method(_blst.P2_serialize) + compress = _swig_new_instance_method(_blst.P2_compress) + on_curve = _swig_new_instance_method(_blst.P2_on_curve) + in_group = _swig_new_instance_method(_blst.P2_in_group) + is_inf = _swig_new_instance_method(_blst.P2_is_inf) + is_equal = _swig_new_instance_method(_blst.P2_is_equal) + aggregate = _swig_new_instance_method(_blst.P2_aggregate) + sign_with = _swig_new_instance_method(_blst.P2_sign_with) + hash_to = _swig_new_instance_method(_blst.P2_hash_to) + encode_to = _swig_new_instance_method(_blst.P2_encode_to) + mult = _swig_new_instance_method(_blst.P2_mult) + cneg = _swig_new_instance_method(_blst.P2_cneg) + neg = _swig_new_instance_method(_blst.P2_neg) + add = _swig_new_instance_method(_blst.P2_add) + dbl = _swig_new_instance_method(_blst.P2_dbl) + generator = _swig_new_static_method(_blst.P2_generator) + __swig_destroy__ = _blst.delete_P2 + +# Register P2 in _blst: +_blst.P2_swigregister(P2) +P2_generator = _blst.P2_generator + +G1 = _blst.G1 +G2 = _blst.G2 +class PT(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.PT_swiginit(self, _blst.new_PT(*args)) + dup = _swig_new_instance_method(_blst.PT_dup) + is_one = _swig_new_instance_method(_blst.PT_is_one) + is_equal = _swig_new_instance_method(_blst.PT_is_equal) + sqr = _swig_new_instance_method(_blst.PT_sqr) + mul = _swig_new_instance_method(_blst.PT_mul) + final_exp = _swig_new_instance_method(_blst.PT_final_exp) + __swig_destroy__ = _blst.delete_PT + +# Register PT in _blst: +_blst.PT_swigregister(PT) + +class Pairing(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self, *args): + _blst.Pairing_swiginit(self, _blst.new_Pairing(*args)) + __swig_destroy__ = _blst.delete_Pairing + aggregate = _swig_new_instance_method(_blst.Pairing_aggregate) + mul_n_aggregate = _swig_new_instance_method(_blst.Pairing_mul_n_aggregate) + commit = _swig_new_instance_method(_blst.Pairing_commit) + merge = _swig_new_instance_method(_blst.Pairing_merge) + finalverify = _swig_new_instance_method(_blst.Pairing_finalverify) + +# Register Pairing in _blst: +_blst.Pairing_swigregister(Pairing) + +cdata = _blst.cdata +memmove = _blst.memmove + +cvar = _blst.cvar +BLS12_381_G1 = cvar.BLS12_381_G1 +BLS12_381_NEG_G1 = cvar.BLS12_381_NEG_G1 +BLS12_381_G2 = cvar.BLS12_381_G2 +BLS12_381_NEG_G2 = cvar.BLS12_381_NEG_G2 + diff --git a/verkle_trie_pedersen/compute_stats.sh b/verkle_trie_pedersen/compute_stats.sh new file mode 100755 index 00000000..e1b16812 --- /dev/null +++ b/verkle_trie_pedersen/compute_stats.sh @@ -0,0 +1,15 @@ +echo -e "WIDTH_BITS\tWIDTH\tNUMBER_INITIAL_KEYS\tNUMBER_KEYS_PROOF\taverage_depth\tproof_size\tproof_time\tcheck_time" > stats.txt + + +python verkle_trie.py 5 65536 500 >> stats.txt +python verkle_trie.py 6 65536 500 >> stats.txt +python verkle_trie.py 7 65536 500 >> stats.txt +python verkle_trie.py 8 65536 500 >> stats.txt +python verkle_trie.py 9 65536 500 >> stats.txt +python verkle_trie.py 10 65536 500 >> stats.txt +python verkle_trie.py 11 65536 500 >> stats.txt +python verkle_trie.py 12 65536 500 >> stats.txt +python verkle_trie.py 13 65536 500 >> stats.txt +python verkle_trie.py 14 65536 500 >> stats.txt +python verkle_trie.py 15 65536 500 >> stats.txt +python verkle_trie.py 16 65536 500 >> stats.txt diff --git a/verkle_trie_pedersen/ipa.py b/verkle_trie_pedersen/ipa.py new file mode 100644 index 00000000..4997dc30 --- /dev/null +++ b/verkle_trie_pedersen/ipa.py @@ -0,0 +1,114 @@ +import blst +import pippenger + +# +# Utilities for dealing with polynomials in evaluation form +# +# A polynomial in evaluation for is defined by its values on DOMAIN, +# where DOMAIN is [omega**0, omega**1, omega**2, ..., omega**(WIDTH-1)] +# where omega is a WIDTH root of unity, i.e. omega**WIDTH % MODULUS == 1 +# +# Any polynomial of degree < WIDTH can be represented uniquely in this form, +# and many operations (such as multiplication and exact division) are more +# efficient. +# +# By precomputing the trusted setup in Lagrange basis, we can also easily +# commit to a a polynomial in evaluation form. +# + +class KzgUtils(): + + """ + Class that defines helper function for Kate proofs in evaluation form (Lagrange basis) + """ + def __init__(self, MODULUS, WIDTH, DOMAIN, SETUP, primefield): + self.MODULUS = MODULUS + self.WIDTH = WIDTH + self.DOMAIN = DOMAIN + self.SETUP = SETUP + self.primefield = primefield + # Precomputed inverses of 1 / (1 - DOMAIN[i]) + self.inverses = [0] + [primefield.inv(1 - DOMAIN[i]) for i in range(1, WIDTH)] + self.inverse_width = primefield.inv(self.WIDTH) + + + def evaluate_polynomial_in_evaluation_form(self, f, z): + """ + Takes a polynomial in evaluation form and evaluates it at one point outside the domain. + Uses the barycentric formula: + f(z) = (1 - z**WIDTH) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i]) + """ + r = 0 + for i in range(self.WIDTH): + r += self.primefield.div(f[i] * self.DOMAIN[i], (z - self.DOMAIN[i]) ) + r = r * (pow(z, self.WIDTH, self.MODULUS) - 1) * self.inverse_width % self.MODULUS + + return r + + + def compute_inner_quotient_in_evaluation_form(self, f, index): + """ + Compute the quotient q(X) = (f(X) - f(DOMAIN[index])) / (X - DOMAIN[index]) in evaluation form. + + Inner means that the value z = DOMAIN[index] is one of the points at which f is evaluated -- so unlike an outer + quotient (where z is not in DOMAIN), we need to do some extra work to compute q[index] where the formula above + is 0 / 0 + """ + q = [0] * self.WIDTH + y = f[index] + for i in range(self.WIDTH): + if i != index: + q[i] = (f[i] - y) * self.DOMAIN[-i] * self.inverses[index - i] % self.MODULUS + q[index] += - self.DOMAIN[(i - index) % self.WIDTH] * q[i] % self.MODULUS + + return q + + + def compute_outer_quotient_in_evaluation_form(self, f, z, y): + """ + Compute the quotient q(X) = (f(X) - y)) / (X - z) in evaluation form. Note that this only works if the quotient + is exact, i.e. f(z) = y, and otherwise returns garbage + """ + q = [0] * self.WIDTH + for i in range(self.WIDTH): + q[i] = self.primefield.div(f[i] - y, self.DOMAIN[i] - z) + + return q + + + def check_kzg_proof(self, C, z, y, pi): + """ + Check the KZG proof + e(C - [y], [1]) = e(pi, [s - z]) + which is equivalent to + e(C - [y], [1]) * e(-pi, [s - z]) == 1 + """ + pairing = blst.PT(blst.G2().to_affine(), C.dup().add(blst.G1().mult(y).neg()).to_affine()) + pairing.mul(blst.PT(self.SETUP["g2"][1].dup().add(blst.G2().mult(z).neg()).to_affine(), pi.dup().neg().to_affine())) + + return pairing.final_exp().is_one() + + + def evaluate_and_compute_kzg_proof(self, f, z): + """ + Evaluates a function f (given in evaluation form) at a point z (which can be in the DOMAIN or not) + and gives y = f(z) as well as a Kate proof that this is the correct result + """ + if z in self.DOMAIN: + index = self.DOMAIN.index(z) + y = f[index] + q = self.compute_inner_quotient_in_evaluation_form(f, index) + else: + y = self.evaluate_polynomial_in_evaluation_form(f, z) + q = self.compute_outer_quotient_in_evaluation_form(f, z, y) + + return y, pippenger.pippenger_simple(self.SETUP["g1_lagrange"], q) + + + def compute_commitment_lagrange(self, values): + """ + Computes a commitment for a function given in evaluation form. + 'values' is a dictionary and can have missing indices, which improves efficiency. + """ + commitment = pippenger.pippenger_simple([self.SETUP["g1_lagrange"][i] for i in values.keys()], values.values()) + return commitment \ No newline at end of file diff --git a/verkle_trie_pedersen/pippenger.py b/verkle_trie_pedersen/pippenger.py new file mode 100644 index 00000000..51d72030 --- /dev/null +++ b/verkle_trie_pedersen/pippenger.py @@ -0,0 +1,68 @@ +import blst +from itertools import zip_longest +from collections import defaultdict +from random import randint +from time import time + +def integer_in_base(i, b): + r = [] + while i > 0: + r.append(i % b) + i //= b + return r + +def pippenger_simple(group_elements, factors): + """ + A naive implementation of a Pippenger-like multiexponentiation algorithm. Don't use this + in practice, a native implementation in the blst library will perform much better. + """ + assert len(group_elements) == len(factors) + n = len(group_elements) + d = 1 + while (d + 2) * 2**(d + 2) < n: + d += 1 + b = 2**d + factors_decomposed = [integer_in_base(factor, b) for factor in factors] + result = blst.P1_generator().mult(0) + for bases in reversed(list(zip_longest(*factors_decomposed, fillvalue=0))): + total = blst.P1_generator().mult(0) + base_elements_dict = defaultdict(list) + for index, base in enumerate(bases): + if base > 0: + base_elements_dict[base].append(group_elements[index]) + for base, base_elements in base_elements_dict.items(): + if len(base_elements) > 0: + sum_base_elements = base_elements[0].dup() + for x in base_elements[1:]: + sum_base_elements.add(x) + sum_base_elements.mult(base) + total.add(sum_base_elements) + result.mult(b).add(total) + return result + +def lincomb_naive(group_elements, factors): + """ + Direct linear combination + """ + assert len(group_elements) == len(factors) + result = blst.P1_generator().mult(0) + for g, f in zip(group_elements, factors): + result.add(g.dup().mult(f)) + return result + +def test_pippenger(group_elements, factors): + """ + Test and time pippenger_simple + """ + time_a = time() + naive_result = lincomb_naive(group_elements, factors) + time_b = time() + print("n = {0} multiexp".format(len(group_elements))) + print("Naive linear combination: {0:.6f} s".format(time_b - time_a)) + pippenger_result = pippenger_simple(group_elements, factors) + time_c = time() + print("Using simple Pippenger algorithm: {0:.6f} s".format(time_c - time_b)) + assert naive_result.is_equal(pippenger_result) + +if __name__ == "__main__": + test_pippenger([blst.P1_generator()]*16384, [randint(0, 2**255) for i in range(16384)]) \ No newline at end of file diff --git a/verkle_trie_pedersen/poly_utils.py b/verkle_trie_pedersen/poly_utils.py new file mode 100644 index 00000000..eb8972d3 --- /dev/null +++ b/verkle_trie_pedersen/poly_utils.py @@ -0,0 +1,298 @@ +# Creates an object that includes convenience operations for numbers +# and polynomials in some prime field + +# Also added interpolation over an arbitrary DOMAIN (not roots of unity) +# + +class PrimeField(): + def __init__(self, MODULUS, WIDTH): + assert pow(2, MODULUS, MODULUS) == 2 + self.WIDTH = WIDTH + self.MODULUS = MODULUS + self.DOMAIN = range(WIDTH) + + self.A = self.zpoly(DOMAIN) + self.Aprime = self.formal_derivative(self.A) + + # i-th Lagrange polynomial + self.lagrange_polys = [] + + # Aprime evaluated on the DOMAIN + self.Aprime_DOMAIN = [] + + # Aprime on the DOMAIN, inverted + self.Aprime_DOMAIN_inv = [] + for i, x in enumerate(DOMAIN): + self.Aprime_DOMAIN.append(self.eval_poly_at(self.Aprime, x)) + self.Aprime_DOMAIN_inv.append(self.inv(self.Aprime_DOMAIN[-1])) + self.lagrange_polys.append(self.mul_polys([self.Aprime_DOMAIN_inv[-1]], + self.div_polys(self.A, [-x, 1]))) + + # Inverses needed for quotients + self.INVERSES = [self.inv(x) for x in list(range(WIDTH)) + list(range(-WIDTH + 1, 0))] + + + def formal_derivative(self, f): + return [(n + 1) * c % self.MODULUS for n, c in enumerate(f[1:])] + + + def evaluate_polynomial_in_evaluation_form(self, f, z): + """ + Takes a polynomial in evaluation form and evaluates it at one point outside the DOMAIN. + Uses the barycentric formula: + f(z) = A(z) * sum_(i=0)^(WIDTH-1) f(DOMAIN[i]) / A'(DOMAIN[i]) * 1 / (z - DOMAIN[i]) + """ + r = 0 + for x, i in enumerate(self.DOMAIN): + r += self.div(f[i], self.Aprime_DOMAIN[i] * (z - x) ) + r = r * self.eval_poly_at(self.A, z) % self.MODULUS + + return r + + + def compute_inner_quotient_in_evaluation_form(self, f, index): + """ + Compute the quotient q(X) = (f(X) - f(DOMAIN[index])) / (X - DOMAIN[index]) in evaluation form. + + Inner means that the value z = DOMAIN[index] is one of the points at which f is evaluated -- so unlike an outer + quotient (where z is not in DOMAIN), we need to do some extra work to compute q[index] where the formula above + is 0 / 0 + """ + q = [0] * self.WIDTH + y = f[index] + for i in range(self.WIDTH): + if i != index: + q[i] = (f[i] - y) * self.inverses[index - i] * self.Aprime_DOMAIN[index] * self.Aprime_DOMAIN_inv[i] % self.MODULUS + q[index] += - self.DOMAIN[(i - index) % self.WIDTH] * q[i] % self.MODULUS + + return q + + + def compute_outer_quotient_in_evaluation_form(self, f, z, y): + """ + Compute the quotient q(X) = (f(X) - y)) / (X - z) in evaluation form. Note that this only works if the quotient + is exact, i.e. f(z) = y, and otherwise returns garbage + """ + q = [0] * self.WIDTH + for i in range(self.WIDTH): + q[i] = self.primefield.div(f[i] - y, self.DOMAIN[i] - z) + + return q + + def add(self, x, y): + return (x+y) % self.MODULUS + + def sub(self, x, y): + return (x-y) % self.MODULUS + + def mul(self, x, y): + return (x*y) % self.MODULUS + + def exp(self, x, p): + return pow(x, p, self.MODULUS) + + # Modular inverse using the extended Euclidean algorithm + def inv(self, a): + if a == 0: + return 0 + lm, hm = 1, 0 + low, high = a % self.MODULUS, self.MODULUS + while low > 1: + r = high//low + nm, new = hm-lm*r, high-low*r + lm, low, hm, high = nm, new, lm, low + return lm % self.MODULUS + + def multi_inv(self, values): + partials = [1] + for i in range(len(values)): + partials.append(self.mul(partials[-1], values[i] or 1)) + inv = self.inv(partials[-1]) + outputs = [0] * len(values) + for i in range(len(values), 0, -1): + outputs[i-1] = self.mul(partials[i-1], inv) if values[i-1] else 0 + inv = self.mul(inv, values[i-1] or 1) + return outputs + + def div(self, x, y): + return self.mul(x, self.inv(y)) + + # Evaluate a polynomial at a point + def eval_poly_at(self, p, x): + y = 0 + power_of_x = 1 + for i, p_coeff in enumerate(p): + y += power_of_x * p_coeff + power_of_x = (power_of_x * x) % self.MODULUS + return y % self.MODULUS + + # Arithmetic for polynomials + def add_polys(self, a, b): + return [((a[i] if i < len(a) else 0) + (b[i] if i < len(b) else 0)) + % self.MODULUS for i in range(max(len(a), len(b)))] + + def sub_polys(self, a, b): + return [((a[i] if i < len(a) else 0) - (b[i] if i < len(b) else 0)) + % self.MODULUS for i in range(max(len(a), len(b)))] + + def mul_by_const(self, a, c): + return [(x*c) % self.MODULUS for x in a] + + def mul_polys(self, a, b): + o = [0] * (len(a) + len(b) - 1) + for i, aval in enumerate(a): + for j, bval in enumerate(b): + o[i+j] += a[i] * b[j] + return [x % self.MODULUS for x in o] + + def div_polys(self, a, b): + assert len(a) >= len(b) + a = [x for x in a] + o = [] + apos = len(a) - 1 + bpos = len(b) - 1 + diff = apos - bpos + while diff >= 0: + quot = self.div(a[apos], b[bpos]) + o.insert(0, quot) + for i in range(bpos, -1, -1): + a[diff+i] -= b[i] * quot + apos -= 1 + diff -= 1 + return [x % self.MODULUS for x in o] + + def mod_polys(self, a, b): + return self.sub_polys(a, self.mul_polys(b, self.div_polys(a, b)))[:len(b)-1] + + # Build a polynomial from a few coefficients + def sparse(self, coeff_dict): + o = [0] * (max(coeff_dict.keys()) + 1) + for k, v in coeff_dict.items(): + o[k] = v % self.MODULUS + return o + + # Build a polynomial that returns 0 at all specified xs + def zpoly(self, xs): + root = [1] + for x in xs: + root.insert(0, 0) + for j in range(len(root)-1): + root[j] -= root[j+1] * x + return [x % self.MODULUS for x in root] + + # Given p+1 y values and x values with no errors, recovers the original + # p+1 degree polynomial. + # Lagrange interpolation works roughly in the following way. + # 1. Suppose you have a set of points, eg. x = [1, 2, 3], y = [2, 5, 10] + # 2. For each x, generate a polynomial which equals its corresponding + # y coordinate at that point and 0 at all other points provided. + # 3. Add these polynomials together. + + def lagrange_interp(self, xs, ys): + # Generate master numerator polynomial, eg. (x - x1) * (x - x2) * ... * (x - xn) + root = self.zpoly(xs) + assert len(root) == len(ys) + 1 + # print(root) + # Generate per-value numerator polynomials, eg. for x=x2, + # (x - x1) * (x - x3) * ... * (x - xn), by dividing the master + # polynomial back by each x coordinate + nums = [self.div_polys(root, [-x, 1]) for x in xs] + # Generate denominators by evaluating numerator polys at each x + denoms = [self.eval_poly_at(nums[i], xs[i]) for i in range(len(xs))] + invdenoms = self.multi_inv(denoms) + # Generate output polynomial, which is the sum of the per-value numerator + # polynomials rescaled to have the right y values + b = [0 for y in ys] + for i in range(len(xs)): + yslice = self.mul(ys[i], invdenoms[i]) + for j in range(len(ys)): + if nums[i][j] and ys[i]: + b[j] += nums[i][j] * yslice + return [x % self.MODULUS for x in b] + + # Optimized poly evaluation for degree 4 + def eval_quartic(self, p, x): + xsq = x * x % self.MODULUS + xcb = xsq * x + return (p[0] + p[1] * x + p[2] * xsq + p[3] * xcb) % self.MODULUS + + # Optimized version of the above restricted to deg-4 polynomials + def lagrange_interp_4(self, xs, ys): + x01, x02, x03, x12, x13, x23 = \ + xs[0] * xs[1], xs[0] * xs[2], xs[0] * xs[3], xs[1] * xs[2], xs[1] * xs[3], xs[2] * xs[3] + m = self.MODULUS + eq0 = [-x12 * xs[3] % m, (x12 + x13 + x23), -xs[1]-xs[2]-xs[3], 1] + eq1 = [-x02 * xs[3] % m, (x02 + x03 + x23), -xs[0]-xs[2]-xs[3], 1] + eq2 = [-x01 * xs[3] % m, (x01 + x03 + x13), -xs[0]-xs[1]-xs[3], 1] + eq3 = [-x01 * xs[2] % m, (x01 + x02 + x12), -xs[0]-xs[1]-xs[2], 1] + e0 = self.eval_poly_at(eq0, xs[0]) + e1 = self.eval_poly_at(eq1, xs[1]) + e2 = self.eval_poly_at(eq2, xs[2]) + e3 = self.eval_poly_at(eq3, xs[3]) + e01 = e0 * e1 + e23 = e2 * e3 + invall = self.inv(e01 * e23) + inv_y0 = ys[0] * invall * e1 * e23 % m + inv_y1 = ys[1] * invall * e0 * e23 % m + inv_y2 = ys[2] * invall * e01 * e3 % m + inv_y3 = ys[3] * invall * e01 * e2 % m + return [(eq0[i] * inv_y0 + eq1[i] * inv_y1 + eq2[i] * inv_y2 + eq3[i] * inv_y3) % m for i in range(4)] + + # Optimized version of the above restricted to deg-2 polynomials + def lagrange_interp_2(self, xs, ys): + m = self.MODULUS + eq0 = [-xs[1] % m, 1] + eq1 = [-xs[0] % m, 1] + e0 = self.eval_poly_at(eq0, xs[0]) + e1 = self.eval_poly_at(eq1, xs[1]) + invall = self.inv(e0 * e1) + inv_y0 = ys[0] * invall * e1 + inv_y1 = ys[1] * invall * e0 + return [(eq0[i] * inv_y0 + eq1[i] * inv_y1) % m for i in range(2)] + + # Optimized version of the above restricted to deg-4 polynomials + def multi_interp_4(self, xsets, ysets): + data = [] + invtargets = [] + for xs, ys in zip(xsets, ysets): + x01, x02, x03, x12, x13, x23 = \ + xs[0] * xs[1], xs[0] * xs[2], xs[0] * xs[3], xs[1] * xs[2], xs[1] * xs[3], xs[2] * xs[3] + m = self.MODULUS + eq0 = [-x12 * xs[3] % m, (x12 + x13 + x23), -xs[1]-xs[2]-xs[3], 1] + eq1 = [-x02 * xs[3] % m, (x02 + x03 + x23), -xs[0]-xs[2]-xs[3], 1] + eq2 = [-x01 * xs[3] % m, (x01 + x03 + x13), -xs[0]-xs[1]-xs[3], 1] + eq3 = [-x01 * xs[2] % m, (x01 + x02 + x12), -xs[0]-xs[1]-xs[2], 1] + e0 = self.eval_quartic(eq0, xs[0]) + e1 = self.eval_quartic(eq1, xs[1]) + e2 = self.eval_quartic(eq2, xs[2]) + e3 = self.eval_quartic(eq3, xs[3]) + data.append([ys, eq0, eq1, eq2, eq3]) + invtargets.extend([e0, e1, e2, e3]) + invalls = self.multi_inv(invtargets) + o = [] + for (i, (ys, eq0, eq1, eq2, eq3)) in enumerate(data): + invallz = invalls[i*4:i*4+4] + inv_y0 = ys[0] * invallz[0] % m + inv_y1 = ys[1] * invallz[1] % m + inv_y2 = ys[2] * invallz[2] % m + inv_y3 = ys[3] * invallz[3] % m + o.append([(eq0[i] * inv_y0 + eq1[i] * inv_y1 + eq2[i] * inv_y2 + eq3[i] * inv_y3) % m for i in range(4)]) + # assert o == [self.lagrange_interp_4(xs, ys) for xs, ys in zip(xsets, ysets)] + return o + + +if __name__ == "__main__": + primefield = PrimeField(11, [0,1,2,3]) + for i, x in enumerate(primefield.DOMAIN): + assert primefield.eval_poly_at(primefield.lagrange_polys[i], x) == 1 + for y in primefield.DOMAIN[:i] + primefield.DOMAIN[i+1:]: + assert primefield.eval_poly_at(primefield.lagrange_polys[i], y) == 0 + + poly = [3, 4, 3, 2] + poly_eval = [primefield.eval_poly_at(poly, x) for x in primefield.DOMAIN] + + assert primefield.eval_poly_at(poly, 5) == primefield.evaluate_polynomial_in_evaluation_form(poly_eval, 5) + + poly_eval_quotient = primefield.compute_inner_quotient_in_evaluation_form(poly_eval, 2) + + poly_quotient = primefield.div_polys([poly[0] - poly_eval[2]] + poly[1:], [-3, 1]) \ No newline at end of file diff --git a/verkle_trie_pedersen/verkle_trie.py b/verkle_trie_pedersen/verkle_trie.py new file mode 100644 index 00000000..5d3158d1 --- /dev/null +++ b/verkle_trie_pedersen/verkle_trie.py @@ -0,0 +1,677 @@ +import pippenger +import blst +import hashlib +from random import randint, shuffle +from poly_utils import PrimeField +from time import time +from kzg_utils import KzgUtils +from fft import fft +import sys + +# +# Proof of concept implementation for verkle tries +# +# All polynomials in this implementation are represented in evaluation form, i.e. by their values +# on DOMAIN. See kzg_utils.py for more explanation +# + +# BLS12_381 curve modulus +MODULUS = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + +# Primitive root for the field +PRIMITIVE_ROOT = 5 + +assert pow(PRIMITIVE_ROOT, (MODULUS - 1) // 2, MODULUS) != 1 +assert pow(PRIMITIVE_ROOT, MODULUS - 1, MODULUS) == 1 + +primefield = PrimeField(MODULUS) + +# Verkle trie parameters +KEY_LENGTH = 256 # bits +WIDTH_BITS = 10 +WIDTH = 2**WIDTH_BITS + +ROOT_OF_UNITY = pow(PRIMITIVE_ROOT, (MODULUS - 1) // WIDTH, MODULUS) +DOMAIN = [pow(ROOT_OF_UNITY, i, MODULUS) for i in range(WIDTH)] + +# Number of key-value pairs to insert +NUMBER_INITIAL_KEYS = 2**15 + +# Number of keys to insert after computing initial tree +NUMBER_ADDED_KEYS = 512 + +# Number of keys to delete +NUMBER_DELETED_KEYS = 512 + +# Number of key/values pair in proof +NUMBER_KEYS_PROOF = 5000 + +def generate_setup(size, secret): + """ + Generates a setup in the G1 group and G2 group, as well as the Lagrange polynomials in G1 (via FFT) + """ + g1_setup = [blst.G1().mult(pow(secret, i, MODULUS)) for i in range(size)] + g2_setup = [blst.G2().mult(pow(secret, i, MODULUS)) for i in range(size)] + g1_lagrange = fft(g1_setup, MODULUS, ROOT_OF_UNITY, inv=True) + return {"g1": g1_setup, "g2": g2_setup, "g1_lagrange": g1_lagrange} + + +def get_verkle_indices(key): + """ + Generates the list of verkle indices for key + """ + x = int.from_bytes(key, "big") + last_index_bits = KEY_LENGTH % WIDTH_BITS + index = (x % (2**last_index_bits)) << (WIDTH_BITS - last_index_bits) + x //= 2**last_index_bits + indices = [index] + for i in range((KEY_LENGTH - 1) // WIDTH_BITS): + index = x % WIDTH + x //= WIDTH + indices.append(index) + return tuple(reversed(indices)) + + +def hash(x): + if isinstance(x, bytes): + return hashlib.sha256(x).digest() + elif isinstance(x, blst.P1): + return hash(x.compress()) + b = b"" + for a in x: + if isinstance(a, bytes): + b += a + elif isinstance(a, int): + b += a.to_bytes(32, "little") + elif isinstance(a, blst.P1): + b += hash(a.compress()) + return hash(b) + + +def hash_to_int(x): + return int.from_bytes(hash(x), "little") + + +def insert_verkle_node(root, key, value): + """ + Insert node without updating hashes/commitments (useful for building a full trie) + """ + current_node = root + indices = iter(get_verkle_indices(key)) + index = None + while current_node["node_type"] == "inner": + previous_node = current_node + previous_index = index + index = next(indices) + if index in current_node: + current_node = current_node[index] + else: + current_node[index] = {"node_type": "leaf", "key": key, "value": value} + return + if current_node["key"] == key: + current_node["value"] = value + else: + previous_node[index] = {"node_type": "inner", "commitment": blst.G1().mult(0)} + insert_verkle_node(root, key, value) + insert_verkle_node(root, current_node["key"], current_node["value"]) + + +def update_verkle_node(root, key, value): + """ + Update or insert node and update all commitments and hashes + """ + current_node = root + indices = iter(get_verkle_indices(key)) + index = None + path = [] + + new_node = {"node_type": "leaf", "key": key, "value": value} + add_node_hash(new_node) + + while True: + index = next(indices) + path.append((index, current_node)) + if index in current_node: + if current_node[index]["node_type"] == "leaf": + old_node = current_node[index] + if current_node[index]["key"] == key: + current_node[index] = new_node + value_change = (MODULUS + int.from_bytes(new_node["hash"], "little") + - int.from_bytes(old_node["hash"], "little")) % MODULUS + break + else: + new_inner_node = {"node_type": "inner"} + new_index = next(indices) + old_index = get_verkle_indices(old_node["key"])[len(path)] + # TODO! Handle old_index == new_index + assert old_index != new_index + new_inner_node[new_index] = new_node + new_inner_node[old_index] = old_node + add_node_hash(new_inner_node) + current_node[index] = new_inner_node + value_change = (MODULUS + int.from_bytes(new_inner_node["hash"], "little") + - int.from_bytes(old_node["hash"], "little")) % MODULUS + break + current_node = current_node[index] + else: + current_node[index] = new_node + value_change = int.from_bytes(new_node["hash"], "little") % MODULUS + break + + # Update all the parent commitments along 'path' + for index, node in reversed(path): + node["commitment"].add(SETUP["g1_lagrange"][index].dup().mult(value_change)) + old_hash = node["hash"] + new_hash = hash(node["commitment"]) + node["hash"] = new_hash + value_change = (MODULUS + int.from_bytes(new_hash, "little") + - int.from_bytes(old_hash, "little")) % MODULUS + + +def get_only_child(node): + """ + Returns the only child of a node which has only one child. Returns 'None' if node has 0 or >1 children + """ + child_count = 0 + only_child = None + for key in node: + if isinstance(key, int): + child_count += 1 + only_child = node[key] + return only_child if child_count == 1 else None + + +def delete_verkle_node(root, key): + """ + Delete node and update all commitments and hashes + """ + current_node = root + indices = iter(get_verkle_indices(key)) + index = None + path = [] + + while True: + index = next(indices) + path.append((index, current_node)) + assert index in current_node, "Tried to delete non-existent key" + if current_node[index]["node_type"] == "leaf": + deleted_node = current_node[index] + assert deleted_node["key"] == key, "Tried to delete non-existent key" + del current_node[index] + value_change = (MODULUS - int.from_bytes(deleted_node["hash"], "little")) % MODULUS + break + current_node = current_node[index] + + # Update all the parent commitments along 'path' + replacement_node = None + for index, node in reversed(path): + if replacement_node != None: + node[index] = replacement_node + replacement_node = None + only_child = get_only_child(node) + if only_child != None and only_child["node_type"] == "leaf" and node != root: + replacement_node = only_child + value_change = (MODULUS + int.from_bytes(only_child["hash"], "little") + - int.from_bytes(node["hash"], "little")) % MODULUS + else: + node["commitment"].add(SETUP["g1_lagrange"][index].dup().mult(value_change)) + old_hash = node["hash"] + new_hash = hash(node["commitment"]) + node["hash"] = new_hash + value_change = (MODULUS + int.from_bytes(new_hash, "little") + - int.from_bytes(old_hash, "little")) % MODULUS + + +def add_node_hash(node): + """ + Recursively adds all missing commitments and hashes to a verkle trie structure. + """ + if node["node_type"] == "leaf": + node["hash"] = hash([node["key"], node["value"]]) + if node["node_type"] == "inner": + lagrange_polynomials = [] + values = {} + for i in range(WIDTH): + if i in node: + if "hash" not in node[i]: + add_node_hash(node[i]) + values[i] = int.from_bytes(node[i]["hash"], "little") + commitment = kzg_utils.compute_commitment_lagrange(values) + node["commitment"] = commitment + node["hash"] = hash(commitment.compress()) + + +def get_total_depth(root): + """ + Computes the total depth (sum of the depth of all nodes) of a verkle trie + """ + if root["node_type"] == "inner": + total_depth = 0 + num_nodes = 0 + for i in range(WIDTH): + if i in root: + depth, nodes = get_total_depth(root[i]) + num_nodes += nodes + total_depth += nodes + depth + return total_depth, num_nodes + else: + return 0, 1 + + +def check_valid_tree(root, is_trie_root=True): + """ + Checks that the tree is valid + """ + if root["node_type"] == "inner": + if not is_trie_root: + only_child = get_only_child(root) + if only_child is not None: + assert only_child["node_type"] == "inner" + + lagrange_polynomials = [] + values = {} + for i in range(WIDTH): + if i in root: + if "hash" not in root[i]: + add_node_hash(node[i]) + values[i] = int.from_bytes(root[i]["hash"], "little") + commitment = kzg_utils.compute_commitment_lagrange(values) + assert root["commitment"].is_equal(commitment) + assert root["hash"] == hash(commitment.compress()) + + for i in range(WIDTH): + if i in root: + check_valid_tree(root[i], False) + else: + assert root["hash"] == hash([root["key"], root["value"]]) + + +def get_average_depth(trie): + """ + Get the average depth of nodes in a verkle trie + """ + depth, nodes = get_total_depth(trie) + return depth / nodes + + +def find_node(root, key): + """ + Finds 'key' in verkle trie. Returns the full node (not just the value) or None if not present + """ + current_node = root + indices = iter(get_verkle_indices(key)) + while current_node["node_type"] == "inner": + index = next(indices) + if index in current_node: + current_node = current_node[index] + else: + return None + if current_node["key"] == key: + return current_node + return None + + +def find_node_with_path(root, key): + """ + As 'find_node', but returns the path of all nodes on the way to 'key' as well as their index + """ + current_node = root + indices = iter(get_verkle_indices(key)) + path = [] + current_index_path = [] + while current_node["node_type"] == "inner": + index = next(indices) + path.append((tuple(current_index_path), index, current_node)) + current_index_path.append(index) + if index in current_node: + current_node = current_node[index] + else: + return path, None + if current_node["key"] == key: + return path, current_node + return path, None + + +def get_proof_size(proof): + depths, commitments_sorted_by_index_serialized, D_serialized, y, sigma_serialized = proof + size = len(depths) # assume 8 bit integer to represent the depth + size += 48 * len(commitments_sorted_by_index_serialized) + size += 48 + 32 + 48 + return size + +lasttime = [0] + + +def start_logging_time_if_eligible(string, eligible): + if eligible: + print(string, file=sys.stderr) + lasttime[0] = time() + + +def log_time_if_eligible(string, width, eligible): + if eligible: + print(string + ' ' * max(1, width - len(string)) + "{0:7.3f} s".format(time() - lasttime[0]), file=sys.stderr) + lasttime[0] = time() + + +def make_kzg_multiproof(Cs, fs, indices, ys, display_times=True): + """ + Computes a KZG multiproof according to the schema described here: + https://notes.ethereum.org/nrQqhVpQRi6acQckwm1Ryg + + zs[i] = DOMAIN[indexes[i]] + """ + + # Step 1: Construct g(X) polynomial in evaluation form + r = hash_to_int([hash(C) for C in Cs] + indices + ys) % MODULUS + + log_time_if_eligible(" Hashed to r", 30, display_times) + + g = [0 for i in range(WIDTH)] + power_of_r = 1 + for f, index in zip(fs, indices): + quotient = kzg_utils.compute_inner_quotient_in_evaluation_form(f, index) + for i in range(WIDTH): + g[i] += power_of_r * quotient[i] + + power_of_r = power_of_r * r % MODULUS + + log_time_if_eligible(" Computed g polynomial", 30, display_times) + + D = kzg_utils.compute_commitment_lagrange({i: v for i, v in enumerate(g)}) + + log_time_if_eligible(" Computed commitment D", 30, display_times) + + # Step 2: Compute f in evaluation form + + t = hash_to_int([r, D]) % MODULUS + + h = [0 for i in range(WIDTH)] + power_of_r = 1 + + for f, index in zip(fs, indices): + denominator_inv = primefield.inv(t - DOMAIN[index]) + for i in range(WIDTH): + h[i] += power_of_r * f[i] * denominator_inv % MODULUS + + power_of_r = power_of_r * r % MODULUS + + log_time_if_eligible(" Computed h polynomial", 30, display_times) + + # Step 3: Evaluate and compute KZG proofs + + y, pi = kzg_utils.evaluate_and_compute_kzg_proof(h, t) + w, rho = kzg_utils.evaluate_and_compute_kzg_proof(g, t) + + + # Compress both proofs into one + + E = kzg_utils.compute_commitment_lagrange({i: v for i, v in enumerate(h)}) + q = hash_to_int([E, D, y, w]) + sigma = pi.dup().add(rho.dup().mult(q)) + + log_time_if_eligible(" Computed KZG proofs", 30, display_times) + + return D.compress(), y, sigma.compress() + + +def check_kzg_multiproof(Cs, indices, ys, proof, display_times=True): + """ + Verifies a KZG multiproof according to the schema described here: + https://notes.ethereum.org/nrQqhVpQRi6acQckwm1Ryg + """ + + D_serialized, y, sigma_serialized = proof + D = blst.P1(D_serialized) + sigma = blst.P1(sigma_serialized) + + # Step 1 + r = hash_to_int([hash(C) for C in Cs] + indices + ys) + + log_time_if_eligible(" Computed r hash", 30, display_times) + + # Step 2 + t = hash_to_int([r, D]) + E_coefficients = [] + g_2_of_t = 0 + power_of_r = 1 + + for index, y in zip(indices, ys): + E_coefficient = primefield.div(power_of_r, t - DOMAIN[index]) + E_coefficients.append(E_coefficient) + g_2_of_t += E_coefficient * y % MODULUS + + power_of_r = power_of_r * r % MODULUS + + log_time_if_eligible(" Computed g2 and e coeffs", 30, display_times) + + E = pippenger.pippenger_simple(Cs, E_coefficients) + + log_time_if_eligible(" Computed E commitment", 30, display_times) + + # Step 3 (Check KZG proofs) + w = (y - g_2_of_t) % MODULUS + + q = hash_to_int([E, D, y, w]) + + if not kzg_utils.check_kzg_proof(E.dup().add(D.dup().mult(q)), t, y + q * w, sigma): + return False + + log_time_if_eligible(" Checked KZG proofs", 30, display_times) + + return True + + +def make_verkle_proof(trie, keys, display_times=True): + """ + Creates a proof for the 'keys' in the verkle trie given by 'trie' + """ + + start_logging_time_if_eligible(" Starting proof computation", display_times) + + # Step 0: Find all keys in the trie + nodes_by_index = {} + nodes_by_index_and_subindex = {} + values = [] + depths = [] + for key in keys: + path, node = find_node_with_path(trie, key) + depths.append(len(path)) + values.append(node["value"]) + for index, subindex, node in path: + nodes_by_index[index] = node + nodes_by_index_and_subindex[(index, subindex)] = node + + log_time_if_eligible(" Computed key paths", 30, display_times) + + # All commitments, but without any duplications. These are for sending over the wire as part of the proof + nodes_sorted_by_index = list(map(lambda x: x[1], sorted(nodes_by_index.items()))) + + # Nodes sorted + nodes_sorted_by_index_and_subindex = list(map(lambda x: x[1], sorted(nodes_by_index_and_subindex.items()))) + + indices = list(map(lambda x: x[0][1], sorted(nodes_by_index_and_subindex.items()))) + + ys = list(map(lambda x: int.from_bytes(x[1][x[0][1]]["hash"], "little"), sorted(nodes_by_index_and_subindex.items()))) + + log_time_if_eligible(" Sorted all commitments", 30, display_times) + + fs = [] + Cs = [x["commitment"] for x in nodes_sorted_by_index_and_subindex] + + for node in nodes_sorted_by_index_and_subindex: + fs.append([int.from_bytes(node[i]["hash"], "little") if i in node else 0 for i in range(WIDTH)]) + + D, y, sigma = make_kzg_multiproof(Cs, fs, indices, ys, display_times) + + commitments_sorted_by_index_serialized = [x["commitment"].compress() for x in nodes_sorted_by_index[1:]] + + log_time_if_eligible(" Serialized commitments", 30, display_times) + + return depths, commitments_sorted_by_index_serialized, D, y, sigma + + +def check_verkle_proof(trie, keys, values, proof, display_times=True): + """ + Checks Verkle tree proof according to + https://notes.ethereum.org/nrQqhVpQRi6acQckwm1Ryg?both + """ + + start_logging_time_if_eligible(" Starting proof check", display_times) + + # Unpack the proof + depths, commitments_sorted_by_index_serialized, D_serialized, y, sigma_serialized = proof + commitments_sorted_by_index = [blst.P1(trie)] + [blst.P1(x) for x in commitments_sorted_by_index_serialized] + + all_indices = set() + all_indices_and_subindices = set() + + leaf_values_by_index_and_subindex = {} + + # Find all required indices + for key, value, depth in zip(keys, values, depths): + verkle_indices = get_verkle_indices(key) + for i in range(depth): + all_indices.add(verkle_indices[:i]) + all_indices_and_subindices.add((verkle_indices[:i], verkle_indices[i])) + leaf_values_by_index_and_subindex[(verkle_indices[:depth - 1], verkle_indices[depth - 1])] = hash([key, value]) + + all_indices = sorted(all_indices) + all_indices_and_subindices = sorted(all_indices_and_subindices) + + log_time_if_eligible(" Computed indices", 30, display_times) + + # Step 0: recreate the commitment list sorted by indices + commitments_by_index = {index: commitment for index, commitment in zip(all_indices, commitments_sorted_by_index)} + commitments_by_index_and_subindex = {index_and_subindex: commitments_by_index[index_and_subindex[0]] + for index_and_subindex in all_indices_and_subindices} + + subhashes_by_index_and_subindex = {} + for index_and_subindex in all_indices_and_subindices: + full_subindex = index_and_subindex[0] + (index_and_subindex[1],) + if full_subindex in commitments_by_index: + subhashes_by_index_and_subindex[index_and_subindex] = hash(commitments_by_index[full_subindex]) + else: + subhashes_by_index_and_subindex[index_and_subindex] = leaf_values_by_index_and_subindex[index_and_subindex] + + Cs = list(map(lambda x: x[1], sorted(commitments_by_index_and_subindex.items()))) + + indices = list(map(lambda x: x[1], sorted(all_indices_and_subindices))) + + ys = list(map(lambda x: int.from_bytes(x[1], "little"), sorted(subhashes_by_index_and_subindex.items()))) + + log_time_if_eligible(" Recreated commitment lists", 30, display_times) + + return check_kzg_multiproof(Cs, indices, ys, [D_serialized, y, sigma_serialized], display_times) + + +if __name__ == "__main__": + if len(sys.argv) > 1: + WIDTH_BITS = int(sys.argv[1]) + WIDTH = 2 ** WIDTH_BITS + ROOT_OF_UNITY = pow(PRIMITIVE_ROOT, (MODULUS - 1) // WIDTH, MODULUS) + DOMAIN = [pow(ROOT_OF_UNITY, i, MODULUS) for i in range(WIDTH)] + + NUMBER_INITIAL_KEYS = int(sys.argv[2]) + + NUMBER_KEYS_PROOF = int(sys.argv[3]) + + NUMBER_DELETED_KEYS = 0 + NUMBER_ADDED_KEYS = 0 + + SETUP = generate_setup(WIDTH, 8927347823478352432985) + kzg_utils = KzgUtils(MODULUS, WIDTH, DOMAIN, SETUP, primefield) + + + # Build a random verkle trie + root = {"node_type": "inner", "commitment": blst.G1().mult(0)} + + values = {} + + for i in range(NUMBER_INITIAL_KEYS): + key = randint(0, 2**256-1).to_bytes(32, "little") + value = randint(0, 2**256-1).to_bytes(32, "little") + insert_verkle_node(root, key, value) + values[key] = value + + average_depth = get_average_depth(root) + + print("Inserted {0} elements for an average depth of {1:.3f}".format(NUMBER_INITIAL_KEYS, average_depth), file=sys.stderr) + + time_a = time() + add_node_hash(root) + time_b = time() + + print("Computed verkle root in {0:.3f} s".format(time_b - time_a), file=sys.stderr) + + if NUMBER_ADDED_KEYS > 0: + + time_a = time() + check_valid_tree(root) + time_b = time() + + print("[Checked tree valid: {0:.3f} s]".format(time_b - time_a), file=sys.stderr) + + time_x = time() + for i in range(NUMBER_ADDED_KEYS): + key = randint(0, 2**256-1).to_bytes(32, "little") + value = randint(0, 2**256-1).to_bytes(32, "little") + update_verkle_node(root, key, value) + values[key] = value + time_y = time() + + print("Additionally inserted {0} elements in {1:.3f} s".format(NUMBER_ADDED_KEYS, time_y - time_x), file=sys.stderr) + print("Keys in tree now: {0}, average depth: {1:.3f}".format(get_total_depth(root)[1], get_average_depth(root)), file=sys.stderr) + + time_a = time() + check_valid_tree(root) + time_b = time() + + print("[Checked tree valid: {0:.3f} s]".format(time_b - time_a), file=sys.stderr) + + if NUMBER_DELETED_KEYS > 0: + + all_keys = list(values.keys()) + shuffle(all_keys) + + keys_to_delete = all_keys[:NUMBER_DELETED_KEYS] + + time_a = time() + for key in keys_to_delete: + delete_verkle_node(root, key) + del values[key] + time_b = time() + + print("Deleted {0} elements in {1:.3f} s".format(NUMBER_DELETED_KEYS, time_b - time_a), file=sys.stderr) + print("Keys in tree now: {0}, average depth: {1:.3f}".format(get_total_depth(root)[1], get_average_depth(root)), file=sys.stderr) + + + time_a = time() + check_valid_tree(root) + time_b = time() + + print("[Checked tree valid: {0:.3f} s]".format(time_b - time_a), file=sys.stderr) + + + all_keys = list(values.keys()) + shuffle(all_keys) + + keys_in_proof = all_keys[:NUMBER_KEYS_PROOF] + + time_a = time() + proof = make_verkle_proof(root, keys_in_proof) + time_b = time() + + proof_size = get_proof_size(proof) + proof_time = time_b - time_a + + print("Computed proof for {0} keys (size = {1} bytes) in {2:.3f} s".format(NUMBER_KEYS_PROOF, proof_size, time_b - time_a), file=sys.stderr) + + time_a = time() + check_verkle_proof(root["commitment"].compress(), keys_in_proof, [values[key] for key in keys_in_proof], proof) + time_b = time() + check_time = time_b - time_a + + print("Checked proof in {0:.3f} s".format(time_b - time_a), file=sys.stderr) + + print("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}".format(WIDTH_BITS, WIDTH, NUMBER_INITIAL_KEYS, NUMBER_KEYS_PROOF, average_depth, proof_size, proof_time, check_time)) \ No newline at end of file