前序博客有:
相关代码见:
测试用例有:
$ pytest
=================================================================== test session starts ====================================================================
platform darwin -- Python 3.9.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/lanyu/zyd/stark-anatomy/code
collected 21 items
test_fast_stark.py F [ 4%]
test_fri.py . [ 9%]
test_ip.py F [ 14%]
test_merkle.py . [ 19%]
test_multivariate.py .. [ 28%]
test_ntt.py ...... [ 57%]
test_rescue_prime.py .. [ 66%]
test_rpsss.py .. [ 76%]
test_stark.py F [ 80%]
test_univariate.py .... [100%]
========================================================================= FAILURES =========================================================================
_____________________________________________________________________ test_fast_stark ______________________________________________________________________
def test_fast_stark( ):
field = Field.main()
expansion_factor = 4
num_colinearity_checks = 2
security_level = 2
rp = RescuePrime()
output_element = field.sample(bytes(b'0xdeadbeef'))
for trial in range(0, 20):
input_element = output_element
print("running trial with input:", input_element.value)
output_element = rp.hash(input_element)
num_cycles = rp.N+1
state_width = rp.m
stark = FastStark(field, expansion_factor, num_colinearity_checks, security_level, state_width, num_cycles)
transition_zerofier, transition_zerofier_codeword, transition_zerofier_root = stark.preprocess()
# prove honestly
print("honest proof generation ...")
# prove
trace = rp.trace(input_element)
air = rp.transition_constraints(stark.omicron)
boundary = rp.boundary_constraints(output_element)
proof = stark.prove(trace, air, boundary, transition_zerofier, transition_zerofier_codeword)
# verify
verdict = stark.verify(proof, air, boundary, transition_zerofier_root)
assert(verdict == True), "valid stark proof fails to verify"
print("success \\o/")
print("verifying false claim ...")
# verify false claim
output_element_ = output_element + field.one()
boundary_ = rp.boundary_constraints(output_element_)
verdict = stark.verify(proof, air, boundary_, transition_zerofier_root)
assert(verdict == False), "invalid stark proof verifies"
print("proof rejected! \\o/")
# prove with false witness
print("attempting to prove with witness violating transition constraints (should not fail because using fast division) ...")
cycle = 1 + (int(os.urandom(1)[0]) % len(trace)-1)
register = int(os.urandom(1)[0]) % state_width
error = field.sample(os.urandom(17))
trace[cycle][register] = trace[cycle][register] + error
> proof = stark.prove(trace, air, boundary, transition_zerofier, transition_zerofier_codeword)
test_fast_stark.py:60:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
fast_stark.py:97: in prove
quotient = (trace_polynomials[s] - interpolant) / zerofier
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <univariate.Polynomial object at 0x10401b400>, other = <univariate.Polynomial object at 0x10401bc10>
def __truediv__( self, other ):
quo, rem = Polynomial.divide(self, other)
> assert(rem.is_zero()), "cannot perform polynomial division because remainder is not zero"
E AssertionError: cannot perform polynomial division because remainder is not zero
univariate.py:52: AssertionError
------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------
running trial with input: 228894434762048332457318
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 58193264164488245767062567324329845625
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 240442014346771117356623971907334212202
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 95249760241169493715201876097323148293
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 45460984185732302940594266735879849714
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 165587666758636920344584355360822203181
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 4978416156855947152397342202659038273
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 31067639684737653452690049877783213369
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 53584232234693103790669953564737900223
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 135431606538579019225905956689564915329
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 112559249994899591330577919758944336055
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 150140559072116678002193196297066253509
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 17074186389844667756333966236643273102
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 82836370955455571146008478781338270694
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 16254124710661698025820423055202445077
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 43769528645735998152380658131815329300
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 211513813789453019359177662714979719288
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 149981298211404449757454909679214149901
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
... but verification should fail :D
proof rejected! \o/
running trial with input: 196164108357361058832227489249577920500
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with witness violating transition constraints (should not fail because using fast division) ...
______________________________________________________________________ test_serialize ______________________________________________________________________
def test_serialize( ):
proof1 = ProofStream()
proof1.push(1)
proof1.push({1: '1'})
proof1.push([1])
proof1.push(2)
serialized = proof1.serialize()
> proof2 = ProofStream.deserialize(serialized)
E TypeError: deserialize() missing 1 required positional argument: 'bb'
test_ip.py:11: TypeError
________________________________________________________________________ test_stark ________________________________________________________________________
def test_stark( ):
field = Field.main()
expansion_factor = 4
num_colinearity_checks = 2
security_level = 2
rp = RescuePrime()
output_element = field.sample(bytes(b'0xdeadbeef'))
for trial in range(0, 20):
input_element = output_element
print("running trial with input:", input_element.value)
output_element = rp.hash(input_element)
num_cycles = rp.N+1
state_width = rp.m
stark = Stark(field, expansion_factor, num_colinearity_checks, security_level, state_width, num_cycles)
# prove honestly
print("honest proof generation ...")
# prove
trace = rp.trace(input_element)
air = rp.transition_constraints(stark.omicron)
boundary = rp.boundary_constraints(output_element)
proof = stark.prove(trace, air, boundary)
# verify
verdict = stark.verify(proof, air, boundary)
assert(verdict == True), "valid stark proof fails to verify"
print("success \\o/")
print("verifying false claim ...")
# verify false claim
output_element_ = output_element + field.one()
boundary_ = rp.boundary_constraints(output_element_)
verdict = stark.verify(proof, air, boundary_)
assert(verdict == False), "invalid stark proof verifies"
print("proof rejected! \\o/")
# verify with false witness
print("attempting to prove with false witness (should fail) ...")
cycle = int(os.urandom(1)[0]) % len(trace)
register = int(os.urandom(1)[0]) % state_width
error = field.sample(os.urandom(17))
trace[cycle][register] = trace[cycle][register] + error
> proof = stark.prove(trace, air, boundary) # should fail
test_stark.py:59:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
stark.py:111: in prove
transition_quotients = [tp / self.transition_zerofier() for tp in transition_polynomials]
stark.py:111: in <listcomp>
transition_quotients = [tp / self.transition_zerofier() for tp in transition_polynomials]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <univariate.Polynomial object at 0x104086130>, other = <univariate.Polynomial object at 0x10407d1c0>
def __truediv__( self, other ):
quo, rem = Polynomial.divide(self, other)
> assert(rem.is_zero()), "cannot perform polynomial division because remainder is not zero"
E AssertionError: cannot perform polynomial division because remainder is not zero
univariate.py:52: AssertionError
------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------
running trial with input: 228894434762048332457318
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 58193264164488245767062567324329845625
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 240442014346771117356623971907334212202
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 95249760241169493715201876097323148293
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 45460984185732302940594266735879849714
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 165587666758636920344584355360822203181
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 4978416156855947152397342202659038273
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 31067639684737653452690049877783213369
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 53584232234693103790669953564737900223
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 135431606538579019225905956689564915329
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 112559249994899591330577919758944336055
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 150140559072116678002193196297066253509
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 17074186389844667756333966236643273102
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 82836370955455571146008478781338270694
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 16254124710661698025820423055202445077
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 43769528645735998152380658131815329300
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 211513813789453019359177662714979719288
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 149981298211404449757454909679214149901
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 196164108357361058832227489249577920500
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
running trial with input: 141980557567619100445655384257862983893
honest proof generation ...
success \o/
verifying false claim ...
proof rejected! \o/
attempting to prove with false witness (should fail) ...
================================================================= short test summary info ==================================================================
FAILED test_fast_stark.py::test_fast_stark - AssertionError: cannot perform polynomial division because remainder is not zero
FAILED test_ip.py::test_serialize - TypeError: deserialize() missing 1 required positional argument: 'bb'
FAILED test_stark.py::test_stark - AssertionError: cannot perform polynomial division because remainder is not zero
======================================================== 3 failed, 18 passed in 2663.71s (0:44:23) =========================================================
$ pytest test_fri.py -s
================================================ test session starts =================================================
platform darwin -- Python 3.9.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/lanyu/zyd/stark-anatomy/code
collected 1 item
test_fri.py testing valid codeword ...
success! \o/
testing invalid codeword ...
last codeword does not correspond to polynomial of low enough degree
observed degree: 127
but should be: 31
success! \o/
.
================================================= 1 passed in 16.26s =================================================
以test_fri.py
为例:
p = 1 + 407 * ( 1 << 119 ) # 1 + 11 * 37 * 2^119
domain
,
ω
\omega
ω对应为代码中的omega
。num_colinearity_tests=17
。num_rounds
,实际计算方式为: def num_rounds( self ):
codeword_length = self.domain_length
num_rounds = 0
while codeword_length > self.expansion_factor and 4*self.num_colinearity_tests < codeword_length:
codeword_length /= 2
num_rounds += 1
return num_rounds
# split and fold
codeword = [two.inverse() * ( (one + alpha / (offset * (omega^i)) ) * codeword[i] + (one - alpha / (offset * (omega^i)) ) * codeword[N//2 + i] ) for i in range(N//2)]
omega = omega^2
offset = offset^2
# compute interpolant
last_domain = [last_offset * (last_omega^i) for i in range(len(last_codeword))]
poly = Polynomial.interpolate_domain(last_domain, last_codeword)
Prover的基本流程为:
1.1)在每一轮中,将该轮codeword构建为Merkle tree,将相应的root存入proof_stream
中——发送给Verifier,将codeword存入codewords
中——供后续提供query证明使用。
# compute and send Merkle root
root = Merkle.commit(codeword)
proof_stream.push(root)
# collect codeword
codewords += [codeword]
而最后一轮,则会将codeword root和codeword本身都发送给Verifier,同时也记录在codewords中。
# send last codeword
proof_stream.push(codeword)
# collect last codeword too
codewords = codewords + [codeword]
1.2)在每一轮的Split-and-Fold过程中,challenge α \alpha α的计算方式为:
# get challenge
alpha = self.field.sample(proof_stream.prover_fiat_shamir())
1.3)当将FRI与STARK一起结合使用时,由于STARK protocol中也定义了Reed-Solomon codeword,可能会存在point evaluation 相交的问题。
因此,定义coset subgroup
D
=
{
g
⋅
ω
i
∣
i
∈
Z
}
D = \lbrace g \cdot \omega^i \vert i \in \mathbb{Z}\rbrace
D={g⋅ωi∣i∈Z},其中
g
g
g为整个multiplicative group
F
\
{
0
}
\mathbb{F} \backslash \lbrace 0\rbrace
F\{0}的generator。而下一轮codeword所选择的evaluation domain 为the set of squares of
D
D
D:
D
⋆
=
{
d
2
∣
d
∈
D
}
=
{
g
2
⋅
ω
2
i
∣
i
∈
Z
}
D^\star = \lbrace d^2 \vert d \in D\rbrace = \lbrace g^2 \cdot \omega^{2i} \vert i \in \mathbb{Z}\rbrace
D⋆={d2∣d∈D}={g2⋅ω2i∣i∈Z}
其中,
g
g
g对应为代码中的generator
和offset
。
因此,在除第一轮之外的各轮中,codeword为对
f
⋆
(
X
)
=
2
−
1
⋅
(
(
1
+
α
X
−
1
)
⋅
f
(
X
)
+
(
1
−
α
X
−
1
)
⋅
f
(
−
X
)
)
f^\star(X) = 2^{-1} \cdot \left( (1 + \alpha X^{-1}) \cdot f(X) + (1 - \alpha X^{-1} ) \cdot f(-X) \right)
f⋆(X)=2−1⋅((1+αX−1)⋅f(X)+(1−αX−1)⋅f(−X)) 基于coset subgroup
D
=
{
g
⋅
ω
i
∣
i
∈
Z
}
D = \lbrace g \cdot \omega^i \vert i \in \mathbb{Z}\rbrace
D={g⋅ωi∣i∈Z}的evaluation值集合,即
{
f
⋆
(
g
⋅
ω
i
)
}
i
=
0
,
⋯
,
N
/
2
\{f^\star(g\cdot \omega^i)\}_{i=0,\cdots,N/2}
{f⋆(g⋅ωi)}i=0,⋯,N/2:
N = len(codeword)
# split and fold
codeword = [two.inverse() * ( (one + alpha / (offset * (omega^i)) ) * codeword[i] + (one - alpha / (offset * (omega^i)) ) * codeword[N//2 + i] ) for i in range(N//2)]
num_colinearity_tests
个不同的query index——top_level_indices
: # get indices
top_level_indices = self.sample_indices(proof_stream.prover_fiat_shamir(), len(codewords[1]), len(codewords[-1]), self.num_colinearity_tests)
indices = [index for index in top_level_indices]
def sample_indices( self, seed, size, reduced_size, number ):
assert(number <= reduced_size), f"cannot sample more indices than available in last codeword; requested: {number}, available: {reduced_size}"
assert(number <= 2*reduced_size), "not enough entropy in indices wrt last codeword"
indices = []
reduced_indices = []
counter = 0
while len(indices) < number:
index = Fri.sample_index(blake2b(seed + bytes(counter)).digest(), size)
reduced_index = index % reduced_size
counter += 1
if reduced_index not in reduced_indices:
indices += [index]
reduced_indices += [reduced_index]
return indices
num_colinearity_tests
次query co-linearity test。 # query phase
for i in range(len(codewords)-1):
indices = [index % (len(codewords[i])//2) for index in indices] # fold
self.query(codewords[i], codewords[i+1], indices, proof_stream)
query
函数,是open相应的A/B/C point的值,以及A/B point在当前codeword的merkle proof,以及C point在下一codeword的merkle proof。open的这些信息均记录在proof_stream
中:def query( self, current_codeword, next_codeword, c_indices, proof_stream ):
# infer a and b indices
a_indices = [index for index in c_indices]
b_indices = [index + len(current_codeword)//2 for index in c_indices]
# reveal leafs
for s in range(self.num_colinearity_tests):
proof_stream.push((current_codeword[a_indices[s]], current_codeword[b_indices[s]], next_codeword[c_indices[s]]))
# reveal authentication paths
for s in range(self.num_colinearity_tests):
proof_stream.push(Merkle.open(a_indices[s], current_codeword))
proof_stream.push(Merkle.open(b_indices[s], current_codeword))
proof_stream.push(Merkle.open(c_indices[s], next_codeword))
return a_indices + b_indices
Verifier的基本流程为:
points = [] # 记录原始codeword对应所有query indices的A/B point值,包含相应的x和y坐标。
verdict = fri.verify(proof_stream, points)
1)从proof_stream
中提取出每一轮的codeword root以及challenge
α
\alpha
α、最后一轮的codeword:
# extract all roots and alphas
roots = []
alphas = []
for r in range(self.num_rounds()):
roots += [proof_stream.pull()]
alphas += [self.field.sample(proof_stream.verifier_fiat_shamir())]
# extract last codeword
last_codeword = proof_stream.pull()
2)检查最后一轮的codeword与最后一个root是否匹配:
# check if it matches the given root
if roots[-1] != Merkle.commit(last_codeword):
print("last codeword is not well formed")
return False
3)检查最后一轮的codeword对应某low degree polynomial f ′ ( X ) f'(X) f′(X),即 deg f ′ ( X ) < ρ ∗ len(last_codeword) \deg_{f'(X)}<\rho * \text{len(last\_codeword)} degf′(X)<ρ∗len(last_codeword)。
# check if it is low degree
degree = (len(last_codeword) // self.expansion_factor) - 1
last_omega
和last_offset
为:last_omega = omega
last_offset = offset
for r in range(self.num_rounds()-1):
last_omega = last_omega^2
last_offset = last_offset^2
last_omega
的order正确:# assert that last_omega has the right order
assert(last_omega.inverse() == last_omega^(len(last_codeword)-1)), "omega does not have right order"
last_omega
和last_offset
组成的last_domain
,对最后一轮的codeword进行插值,获得相应的多项式
f
′
(
X
)
f'(X)
f′(X),对应代码中的poly
:# compute interpolant
last_domain = [last_offset * (last_omega^i) for i in range(len(last_codeword))]
poly = Polynomial.interpolate_domain(last_domain, last_codeword)
#coefficients = intt(last_omega, last_codeword)
#poly = Polynomial(coefficients).scale(last_offset.inverse())
last_domain
,对多项式
f
′
(
X
)
f'(X)
f′(X)进行evaluate,反向验证所有evaluation值 与 最后一轮的codeword对应。# verify by evaluating
assert(poly.evaluate_domain(last_domain) == last_codeword), "re-evaluated codeword does not match original!"
degree
:if poly.degree() > degree:
print("last codeword does not correspond to polynomial of low enough degree")
print("observed degree:", poly.degree())
print("but should be:", degree)
return False
4)根据query complexity 获取num_colinearity_tests
个不同的query index——top_level_indices
:【此时的size
和reduced_size
分别为self.domain_length >> 1
和self.domain_length >> (self.num_rounds()-1)
。】
# get indices
top_level_indices = self.sample_indices(proof_stream.verifier_fiat_shamir(), self.domain_length >> 1, self.domain_length >> (self.num_rounds()-1), self.num_colinearity_tests)
def sample_indices( self, seed, size, reduced_size, number ):
assert(number <= reduced_size), f"cannot sample more indices than available in last codeword; requested: {number}, available: {reduced_size}"
assert(number <= 2*reduced_size), "not enough entropy in indices wrt last codeword"
indices = []
reduced_indices = []
counter = 0
while len(indices) < number:
index = Fri.sample_index(blake2b(seed + bytes(counter)).digest(), size)
reduced_index = index % reduced_size
counter += 1
if reduced_index not in reduced_indices:
indices += [index]
reduced_indices += [reduced_index]
return indices
5)验证每一轮之间的所有query indices 的co-linearity check,以及,验证每一轮的所有query indices的A/B/C point的Merkle proof:
# for every round, check consistency of subsequent layers
for r in range(0, self.num_rounds()-1):
# fold c indices
c_indices = [index % (self.domain_length >> (r+1)) for index in top_level_indices]
# infer a and b indices
a_indices = [index for index in c_indices]
b_indices = [index + (self.domain_length >> (r+1)) for index in a_indices]
# read values and check colinearity
aa = []
bb = []
cc = []
for s in range(self.num_colinearity_tests):
(ay, by, cy) = proof_stream.pull()
aa += [ay]
bb += [by]
cc += [cy]
# record top-layer values for later verification
if r == 0:
polynomial_values += [(a_indices[s], ay), (b_indices[s], by)]
# colinearity check
ax = offset * (omega^a_indices[s])
bx = offset * (omega^b_indices[s])
cx = alphas[r]
if colinearity_test([(ax, ay), (bx, by), (cx, cy)]) == False:
print("colinearity check failure")
return False
# verify authentication paths
for i in range(self.num_colinearity_tests):
path = proof_stream.pull()
if Merkle.verify(roots[r], a_indices[i], path, aa[i]) == False:
print("merkle authentication path verification fails for aa")
return False
path = proof_stream.pull()
if Merkle.verify(roots[r], b_indices[i], path, bb[i]) == False:
print("merkle authentication path verification fails for bb")
return False
path = proof_stream.pull()
if Merkle.verify(roots[r+1], c_indices[i], path, cc[i]) == False:
print("merkle authentication path verification fails for cc")
return False
# square omega and offset to prepare for next round
omega = omega^2
offset = offset^2
def colinearity_test( points ):
domain = [p[0] for p in points]
values = [p[1] for p in points]
polynomial = Polynomial.interpolate_domain(domain, values)
return polynomial.degree() == 1
6)第一轮的codeword为基于 D = { ω i } D=\{\omega^i\} D={ωi}对 f ( X ) f(X) f(X)进行evaluate,而除此之外的每一轮,都是基于 D ⋆ = { g ω i } D^\star=\{g\omega^i\} D⋆={gωi}对 f ⋆ ( X ) f^\star(X) f⋆(X)进行evaluate。在步骤5)中已对 D ⋆ = { g ω i } D^\star=\{g\omega^i\} D⋆={gωi}各轮之间的co-linearity进行了check。接下来是校验第一轮codeword的值:
points = [] # 记录原始codeword对应所有query indices的A/B point值,包含相应的x和y坐标。
verdict = fri.verify(proof_stream, points)
if verdict == False:
print("rejecting proof, but proof should be valid!")
return
for (x,y) in points: # 验证第一轮的codeword值是否正确。
if polynomial.evaluate(omega^x) != y:
print("polynomial evaluates to wrong value")
assert(False)
# -----------------------------
def verify( self, proof_stream, polynomial_values ):
.....
# get indices
top_level_indices = self.sample_indices(proof_stream.verifier_fiat_shamir(), self.domain_length >> 1, self.domain_length >> (self.num_rounds()-1), self.num_colinearity_tests)
# for every round, check consistency of subsequent layers
for r in range(0, self.num_rounds()-1):
# fold c indices
c_indices = [index % (self.domain_length >> (r+1)) for index in top_level_indices]
# infer a and b indices
a_indices = [index for index in c_indices]
b_indices = [index + (self.domain_length >> (r+1)) for index in a_indices]
# read values and check colinearity
aa = []
bb = []
cc = []
for s in range(self.num_colinearity_tests):
(ay, by, cy) = proof_stream.pull()
aa += [ay]
bb += [by]
cc += [cy]
# record top-layer values for later verification
if r == 0:
polynomial_values += [(a_indices[s], ay), (b_indices[s], by)]
.......