mirror of
https://github.com/status-im/research.git
synced 2025-01-11 15:44:08 +00:00
Added 2-of-3 and TRIBES low influence functions
This commit is contained in:
parent
f49c584f83
commit
1830ae2091
@ -1,10 +1,12 @@
|
||||
def compress_fri(prf):
|
||||
o = []
|
||||
oindex = {}
|
||||
def add_obj(x):
|
||||
if x in o:
|
||||
o.append(o.index(x).to_bytes(2, 'big'))
|
||||
if x in oindex:
|
||||
o.append(oindex[x].to_bytes(2, 'big'))
|
||||
else:
|
||||
o.append(x)
|
||||
oindex[x] = len(o)-1
|
||||
|
||||
for root, yproofs in prf[:-1]:
|
||||
# print('Adding proof item, pos %d' % len(o))
|
||||
@ -54,11 +56,13 @@ def decompress_fri(proof):
|
||||
|
||||
def compress_branches(branches):
|
||||
o = []
|
||||
oindex = {}
|
||||
def add_obj(x):
|
||||
if x in o:
|
||||
o.append(o.index(x).to_bytes(2, 'big'))
|
||||
if x in oindex:
|
||||
o.append(oindex[x].to_bytes(2, 'big'))
|
||||
else:
|
||||
o.append(x)
|
||||
oindex[x] = len(o)-1
|
||||
|
||||
for branch in branches:
|
||||
for p in branch:
|
||||
|
@ -1,13 +1,15 @@
|
||||
def _simple_ft(vals, modulus, roots_of_unity):
|
||||
L = len(roots_of_unity)
|
||||
o = [0 for _ in range(L)]
|
||||
o = []
|
||||
for i in range(L):
|
||||
last = 0
|
||||
for j in range(L):
|
||||
o[i] += vals[j] * roots_of_unity[(i*j)%L]
|
||||
return [x % modulus for x in o]
|
||||
last += vals[j] * roots_of_unity[(i*j)%L]
|
||||
o.append(last % modulus)
|
||||
return o
|
||||
|
||||
def _fft(vals, modulus, roots_of_unity):
|
||||
if len(vals) == 1:
|
||||
if len(vals) <= 1:
|
||||
return vals
|
||||
# return _simple_ft(vals, modulus, roots_of_unity)
|
||||
L = _fft(vals[::2], modulus, roots_of_unity[::2])
|
||||
|
@ -1,6 +1,5 @@
|
||||
from merkle_tree import merkelize, mk_branch, verify_branch
|
||||
from utils import get_power_cycle, get_pseudorandom_indices
|
||||
from fft import fft
|
||||
from poly_utils import PrimeField
|
||||
|
||||
# Generate an FRI proof that the polynomial that has the specified
|
||||
@ -33,15 +32,14 @@ def prove_low_degree(values, root_of_unity, maxdeg_plus_1, modulus, exclude_mult
|
||||
|
||||
# Calculate the "column" at that x coordinate
|
||||
# (see https://vitalik.ca/general/2017/11/22/starks_part_2.html)
|
||||
# We calculate the column by Lagrange-interpolating the row, and not
|
||||
# We calculate the column by Lagrange-interpolating each row, and not
|
||||
# directly from the polynomial, as this is more efficient
|
||||
column = []
|
||||
for i in range(len(xs)//4):
|
||||
x_poly = f.lagrange_interp_4(
|
||||
[xs[i+len(xs)*j//4] for j in range(4)],
|
||||
[values[i+len(values)*j//4] for j in range(4)],
|
||||
)
|
||||
column.append(f.eval_poly_at(x_poly, special_x))
|
||||
quarter_len = len(xs)//4
|
||||
x_polys = f.multi_interp_4(
|
||||
[[xs[i+quarter_len*j] for j in range(4)] for i in range(quarter_len)],
|
||||
[[values[i+quarter_len*j] for j in range(4)] for i in range(quarter_len)]
|
||||
)
|
||||
column = [f.eval_quartic(p, special_x) for p in x_polys]
|
||||
m2 = merkelize(column)
|
||||
|
||||
# Pseudo-randomly select y indices to sample
|
||||
@ -56,11 +54,6 @@ def prove_low_degree(values, root_of_unity, maxdeg_plus_1, modulus, exclude_mult
|
||||
# This component of the proof
|
||||
o = [m2[1], branches]
|
||||
|
||||
# Interpolate the polynomial for the column
|
||||
# sub_xs = [xs[i] for i in range(0, len(xs), 4)]
|
||||
# ypoly = fft(column[:len(sub_xs)], modulus,
|
||||
# f.exp(root_of_unity, 4), inv=True)
|
||||
|
||||
# Recurse...
|
||||
return [o] + prove_low_degree(column, f.exp(root_of_unity, 4),
|
||||
maxdeg_plus_1 // 4, modulus, exclude_multiples_of=exclude_multiples_of)
|
||||
@ -94,24 +87,30 @@ def verify_low_degree_proof(merkle_root, root_of_unity, proof, maxdeg_plus_1, mo
|
||||
ys = get_pseudorandom_indices(root2, roudeg // 4, 40,
|
||||
exclude_multiples_of=exclude_multiples_of)
|
||||
|
||||
# Verify for each selected y coordinate that the four points from the
|
||||
# polynomial and the one point from the column that are on that y
|
||||
# coordinate are on the same deg < 4 polynomial
|
||||
# For each y coordinate, get the x coordinates on the row, the values on
|
||||
# the row, and the value at that y from the column
|
||||
xcoords = []
|
||||
rows = []
|
||||
columnvals = []
|
||||
for i, y in enumerate(ys):
|
||||
# The x coordinates from the polynomial
|
||||
x1 = f.exp(root_of_unity, y)
|
||||
xcoords = [(quartic_roots_of_unity[j] * x1) % modulus for j in range(4)]
|
||||
xcoords.append([(quartic_roots_of_unity[j] * x1) % modulus for j in range(4)])
|
||||
|
||||
# The values from the polynomial
|
||||
# The values from the original polynomial
|
||||
row = [verify_branch(merkle_root, y + (roudeg // 4) * j, prf)
|
||||
for j, prf in zip(range(4), branches[i][1:])]
|
||||
rows.append(row)
|
||||
|
||||
# Verify proof and recover the column value
|
||||
values = [verify_branch(root2, y, branches[i][0])] + row
|
||||
columnvals.append(verify_branch(root2, y, branches[i][0]))
|
||||
|
||||
# Lagrange interpolate and check deg is < 4
|
||||
p = f.lagrange_interp_4(xcoords, row)
|
||||
assert f.eval_poly_at(p, special_x) == verify_branch(root2, y, branches[i][0])
|
||||
# Verify for each selected y coordinate that the four points from the
|
||||
# polynomial and the one point from the column that are on that y
|
||||
# coordinate are on the same deg < 4 polynomial
|
||||
polys = f.multi_interp_4(xcoords, rows)
|
||||
|
||||
for p, c in zip(polys, columnvals):
|
||||
assert f.eval_quartic(p, special_x) == c
|
||||
|
||||
# Update constants to check the next proof
|
||||
merkle_root = root2
|
||||
|
@ -126,6 +126,12 @@ class PrimeField():
|
||||
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 = \
|
||||
@ -159,3 +165,33 @@ class PrimeField():
|
||||
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
|
||||
|
61
randao_analysis/low_influence/2of3.py
Normal file
61
randao_analysis/low_influence/2of3.py
Normal file
@ -0,0 +1,61 @@
|
||||
# Implements the "iterated 2-of-3 majority" low-influence function
|
||||
# from https://arxiv.org/pdf/1406.5694.pdf and outputs the probability
|
||||
# that any specific user will be able to influence the result
|
||||
|
||||
import random
|
||||
|
||||
def mkbits(depth):
|
||||
return random.randrange(2**(3**depth))
|
||||
|
||||
def winner(val, depth):
|
||||
if depth == 0:
|
||||
return val & 1
|
||||
subwinners = [
|
||||
winner(val, depth-1),
|
||||
winner(val >> (3**(depth-1)), depth-1),
|
||||
winner(val >> (3**(depth-1) * 2), depth-1)
|
||||
]
|
||||
return 1 if sum(subwinners) >= 2 else 0
|
||||
|
||||
def is_marginal(val, depth):
|
||||
if depth == 0:
|
||||
return True
|
||||
s1, s2, s3 = val, val >> (3**(depth-1)), val >> (3**(depth-1) * 2)
|
||||
w1, w2, w3 = (
|
||||
winner(s1, depth-1),
|
||||
winner(s2, depth-1),
|
||||
winner(s3, depth-1)
|
||||
)
|
||||
if w1 == w2 and w2 == w3:
|
||||
return False
|
||||
|
||||
dominants = (s2, s3) if (w1 != w2 and w1 != w3) else \
|
||||
(s1, s3) if (w2 != w1 and w2 != w3) else \
|
||||
(s1, s2) if (w3 != w1 and w3 != w2) else \
|
||||
False
|
||||
|
||||
return is_marginal(dominants[0], depth-1) or \
|
||||
is_marginal(dominants[1], depth-1)
|
||||
|
||||
def is_marginal2(val, depth):
|
||||
w = winner(val, depth)
|
||||
for x in range(3**depth):
|
||||
val2 = val ^ (1 << x)
|
||||
w2 = winner(val2, depth)
|
||||
if w2 != w:
|
||||
return True
|
||||
return False
|
||||
|
||||
def influence(val, depth):
|
||||
tot = 0
|
||||
w = winner(val, depth)
|
||||
for i in range(3**depth):
|
||||
val2 = val ^ (1 << i)
|
||||
w2 = winner(val2, depth)
|
||||
if w != w2:
|
||||
tot += 1
|
||||
return tot / (3**depth)
|
||||
|
||||
bitz = [mkbits(3) for i in range(1000)]
|
||||
|
||||
print(sum([influence(b, 3) for b in bitz]) / 1000)
|
46
randao_analysis/low_influence/tribes.py
Normal file
46
randao_analysis/low_influence/tribes.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Implements a modified version of the TRIBES low-influence function
|
||||
# mentioned in https://arxiv.org/pdf/1406.5694.pdf and outputs the
|
||||
# probability that any specific user will be able to influence the result
|
||||
|
||||
import random, math
|
||||
|
||||
def mkbits(n):
|
||||
return random.randrange(2**n)
|
||||
|
||||
def tribes_log(n):
|
||||
w = 1
|
||||
while w * 2**w * 693 < n * 1000:
|
||||
w += 1
|
||||
return w
|
||||
|
||||
def tribes(val, n):
|
||||
split = tribes_log(n)
|
||||
o = []
|
||||
full_subset = (1 << split) - 1
|
||||
for i in range(n):
|
||||
vall = val ^ ((2*i+3)**n % 2**n)
|
||||
t = 0
|
||||
for _ in range(n // split):
|
||||
if vall & full_subset == full_subset:
|
||||
t = 1
|
||||
break
|
||||
vall >>= split
|
||||
o.append(t)
|
||||
if len(o) % 2 == 0 and o[-2] == 0 and o[-1] == 1:
|
||||
return 0
|
||||
if len(o) % 2 == 0 and o[-2] == 1 and o[-1] == 0:
|
||||
return 1
|
||||
return o[-1]
|
||||
|
||||
def influence(val, n):
|
||||
tot = 0
|
||||
w = tribes(val, n)
|
||||
for i in range(n):
|
||||
val2 = val ^ (1 << i)
|
||||
w2 = tribes(val2, n)
|
||||
if w != w2:
|
||||
tot += 1
|
||||
return tot / n
|
||||
|
||||
print(sum([influence(mkbits(50), 50) for i in range(1000)]) / 1000)
|
||||
# print(sum([tribes(mkbits(50), 50) for i in range(1000)]) / 1000)
|
Loading…
x
Reference in New Issue
Block a user