Skip to content

Commit

Permalink
feat(circle.py): accomplish
Browse files Browse the repository at this point in the history
  • Loading branch information
ahy231 committed Nov 7, 2024
1 parent 4e8250e commit 5f78511
Showing 1 changed file with 171 additions and 12 deletions.
183 changes: 171 additions & 12 deletions src/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
sys.path.append("../src")

from utils import log_2
from merkle import MerkleTree
from merkle import MerkleTree, verify_decommitment
from merlin.merlin_transcript import MerlinTranscript

F31 = GF(31)
Expand Down Expand Up @@ -270,7 +270,7 @@ def extrapolate(cls, evals, blowup_factor):

class FRI:
@classmethod
def fold_y(cls, evals, domain, r, debug=False):
def fold_y(cls, evals, domain, beta, debug=False):
# first step (J mapping)
# for f in natural order, we just divide f into 2 parts from the middle
N = len(evals)
Expand All @@ -283,30 +283,169 @@ def fold_y(cls, evals, domain, r, debug=False):
for i, (_, y) in enumerate(domain[:N//2]):
f0 = (left[i] + right[i]) / C31(2)
f1 = (left[i] - right[i]) / (C31(2) * y)
evals[i] = f0 + r * f1
if debug: print(f"f[{i}] = {evals[i]} = ({left[i]} + {right[i]})/2 + {r} * ({left[i]} - {right[i]})/(2 * {y})")
evals[i] = f0 + beta * f1
if debug: print(f"f[{i}] = {evals[i]} = ({left[i]} + {right[i]})/2 + {beta} * ({left[i]} - {right[i]})/(2 * {y})")

return evals

# Inputs:
# f is the polynomial to be folded
# D is the domain of f
# r is the random number for random linear combination
# Outputs:
# The first return value is the folded polynomial
# The second return value is the new domain
@classmethod
def commit_phase(cls, input, transcript, debug=False):
def fold_x(cls, f, D, r, debug=False):
assert len(f) == len(D), "len(f) != len(D), {} != {}, f={}, D={}".format(len(f), len(D), f, D)

# divide
N = len(f)
# left is the first half of f, of x from 1 to g^(N/2)
left = f[:N//2]
# right is the second half of f, of x from g^(N-1) to g^(N/2), which corresponds to minus x in left
right = f[:N//2-1:-1]
assert len(left) == len(right), "len(left) != len(right), {} != {}, left={}, right={}".format(len(left), len(right), left, right)

for i, x in enumerate(D[:N//2]):
# f == f0 + x * f1
f0 = (left[i] + right[i]) / C31(2)
f1 = (left[i] - right[i]) / (C31(2) * x)
# f[:N//2] stores the folded polynomial
if debug: print(f"f[{i}] = {f[i]} = ({left[i]} + {right[i]})/2 + {r} * ({left[i]} - {right[i]})/(2 * {x})")
f[i] = f0 + r * f1
# if debug: print(f"{f[i]} = ({left[i]} + {right[i]})/2 + {r} * ({left[i]} - {right[i]})/(2 * {x})")
# reuse f[N//2:] to store new domain
f[N//2 + i] = 2 * x^2 - 1

# return the folded polynomial and the new domain
return f[:N//2], f[N//2:]

@classmethod
def commit_phase(cls, input, domain, transcript, debug=False):
assert isinstance(transcript, MerlinTranscript), "transcript should be a MerlinTranscript"
folded = input

# fold
trees = []
oracles = []
for _ in range(log_2(len(input))):
# random number
transcript.append_message(b'tree', bytes(trees[-1].root, 'ascii'))

beta = int.from_bytes(transcript.challenge_bytes(b'beta', int(4)), 'big')
folded, domain = cls.fold_x(folded, domain, beta, debug)

if debug: print(f"f={folded}, D={domain}")

# merkle tree
trees.append(MerkleTree(folded))
oracles.append(folded[:])

final_poly = folded[0]
if debug:
for x in folded:
assert final_poly == x, "final_poly != x, {} != {}, final_poly={}, x={}".format(final_poly, x, final_poly, x)
transcript.append_message(b"final_poly", bytes(final_poly, 'ascii'))

return {
"commits": [tree.root for tree in trees],
"trees": trees,
"oracles": oracles,
"final_poly": final_poly
}

@classmethod
def prove(cls, input, transcript, open_input, debug=False):
def answer_query(cls, trees, oracles, index, degree, debug=False):
opening_proofs = []
sibling_values = []
for i, tree in enumerate(trees):
index_i = index >> i
index_i_sibling = 1 << degree - 1 - index_i
degree >>= 1

tree = trees[i]
oracle = oracles[i]

assert isinstance(tree, MerkleTree), "tree should be a MerkleTree"

opening_proofs.append((tree.get_authentication_path(index_i), tree.get_authentication_path(index_i_sibling)))
sibling_values.append(oracle[index_i_sibling])

if debug:
assert verify_decommitment(index_i, oracle[index_i], opening_proofs[0], tree.root), "verify_decommitment failed"
assert verify_decommitment(index_i_sibling, oracle[index_i_sibling], opening_proofs[1], tree.root), "verify_decommitment failed"

return zip(opening_proofs, sibling_values)

@classmethod
def prove(cls, input, domain, transcript, open_input, num_queries, debug=False):
assert isinstance(transcript, MerlinTranscript), "transcript should be a MerlinTranscript"

degree = len(input)

# commit phase
commit_phase_result = cls.commit_phase(input, domain, transcript, debug)

# query phase
sample = lambda: int.from_bytes(transcript.challenge_bytes(b"query", 4), "big")
queries = [sample() for _ in range(num_queries)]

query_proofs = []
for query in queries:
query_proofs.append({
"input_proof": open_input(query),
"commit_phase_openings": cls.answer_query(commit_phase_result["trees"], commit_phase_result["oracles"], query >> (32 - log_2(degree)), degree, debug)
})

return {
"commit_phase_commits": commit_phase_result["commits"],
"query_proofs": query_proofs,
"final_poly": commit_phase_result["final_poly"]
}

@classmethod
def verify_query(cls):
pass
def verify_query(cls, index, steps, reduced_opening, log_max_height):
folded_eval = reduced_opening
for log_folded_height, (beta, comm, opening) in zip(range(log_max_height)[::-1], steps):
index_sibling = 1 << log_folded_height - 1 - index

opening_proofs = opening[0]
sibling_values = opening[1]

assert verify_decommitment(index, folded_eval, opening_proofs[0], comm), "verify_decommitment failed"
assert verify_decommitment(index_sibling, sibling_values, opening_proofs[1], comm), "verify_decommitment failed"

domain = CirclePCS.natural_domain_for_degree(1 << log_folded_height)
x = domain[index]
left = folded_eval if index < index_sibling else sibling_values
right = sibling_values if index < index_sibling else folded_eval
folded_eval = (left + right) / C31(2) + beta * ((left - right) / (C31(2) * x))

return folded_eval

@classmethod
def verify(cls):
pass
def verify(cls, proof, transcript, open_input, debug=False):
assert isinstance(transcript, MerlinTranscript), "transcript should be a MerlinTranscript"

betas = []
for i in range(len(proof["trees"])):
transcript.append_message(b"tree", bytes(proof["commit_phase_commits"][i], 'ascii'))
beta = transcript.challenge_bytes(b"beta", 4)
beta = int.from_bytes(beta, "big")
betas.append(beta)
transcript.append_message(b"final_poly", bytes(proof["final_poly"], 'ascii'))

folded_eval = 0
for qp in proof["query_proofs"]:
index = transcript.challenge_bytes(b"query", 4)
index = int.from_bytes(index, "big")
ro = open_input(index, qp["input_proof"])

log_max_height = len(proof["commit_phase_commits"])
folded_eval = cls.verify_query(index >> (32 - log_max_height), zip(betas, proof["commit_phase_commits"], qp["commit_phase_openings"]), ro, log_max_height)

assert folded_eval == proof["final_poly"], "folded_eval != proof['final_poly'], {} != {}".format(folded_eval, proof["final_poly"])

class CirclePCS:
G5 = [g**k for k in range(32)]
Expand Down Expand Up @@ -337,7 +476,7 @@ def commit(cls, eval, domain, blowup_factor):
return tree.root, tree

@classmethod
def open(cls, evals, tree, zeta, log_blowup, transcript, debug=False):
def open(cls, evals, evals_commit, zeta, log_blowup, transcript, num_queries, debug=False):
assert isinstance(transcript, MerlinTranscript), "transcript should be a MerlinTranscript"
alpha = transcript.challenge_bytes(b"alpha", 4)
alpha = int.from_bytes(alpha, "big")
Expand All @@ -361,11 +500,31 @@ def open(cls, evals, tree, zeta, log_blowup, transcript, debug=False):
# fold first layer
# a little modified compared to code in p3
fri_input = FRI.fold_y(first_layer, domain, bivariate_beta, debug)
domain = sq(domain)

def open_input(index):
index >>= 32 - log_2(len(evals))
assert isinstance(evals_commit, MerkleTree), "evals_commit should be a MerkleTree"
input_opening = (evals_commit.get_authentication_path(index), evals[index])

if debug:
assert verify_decommitment(index, evals[index], input_opening, evals_commit.root), "verify_decommitment failed"

first_layer_index = index
first_layer_index_sibling = first_layer_index ^ 1
first_layer_proof = (first_layer_tree.get_authentication_path(first_layer_index), first_layer_tree.get_authentication_path(first_layer_index_sibling))

return {
"input_proof": input_opening,
"first_layer_proof": first_layer_proof,
"first_layer_sibling_value": first_layer[first_layer_index_sibling]
}

fri_proof = FRI.prove(fri_input)
fri_proof = FRI.prove(fri_input, domain, transcript, open_input, num_queries, debug)

return {
"first_layer_commitment": first_layer_tree.root,
"lambda": lambda_,
"fri_proof": fri_proof
}

Expand Down

0 comments on commit 5f78511

Please sign in to comment.