├── fft-notes.pdf ├── notes_halo.pdf ├── notes_nova.pdf ├── notes_ntt.pdf ├── notes_caulk.pdf ├── notes_sonic.pdf ├── weil-pairing.pdf ├── notes_bls-sig.pdf ├── notes_fri_stir.pdf ├── notes_hypernova.pdf ├── notes_spartan.pdf ├── sigma-or-notes.pdf ├── seminarexercises.pdf ├── galois-theory-notes.pdf ├── notes_reed-solomon.pdf ├── slides_hypernova-part1-introduction.pdf ├── abstract-algebra-charles-pinter-notes.pdf ├── slides_hypernova-part2-multifolding-unfolded.pdf ├── typos.toml ├── .gitignore ├── fft-notes.bib ├── notes_reed-solomon.bib ├── README.md ├── bls-sigs.sage ├── kzg.sage ├── blind-sign-over-ec.sage ├── number-theory.sage ├── powersoftau.sage ├── ccs-r1cs.sage ├── notes_bls-sig.tex ├── bls12-381.sage ├── ccs-plonk.sage ├── seminarexercises.tex ├── fft.sage ├── ring-signatures.sage ├── fft-notes.tex ├── notes_sonic.tex ├── paper-notes.bib ├── sigma-or-notes.tex ├── notes_halo.tex ├── sigma.sage ├── notes_ntt.tex ├── weil-pairing.tex ├── notes_reed-solomon.tex ├── notes_fri_stir.tex ├── slides_hypernova-part2-multifolding-unfolded.tex ├── notes_nova.tex ├── slides_hypernova-part1-introduction.tex ├── notes_spartan.tex └── ipa.sage /fft-notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/fft-notes.pdf -------------------------------------------------------------------------------- /notes_halo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_halo.pdf -------------------------------------------------------------------------------- /notes_nova.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_nova.pdf -------------------------------------------------------------------------------- /notes_ntt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_ntt.pdf -------------------------------------------------------------------------------- /notes_caulk.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_caulk.pdf -------------------------------------------------------------------------------- /notes_sonic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_sonic.pdf -------------------------------------------------------------------------------- /weil-pairing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/weil-pairing.pdf -------------------------------------------------------------------------------- /notes_bls-sig.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_bls-sig.pdf -------------------------------------------------------------------------------- /notes_fri_stir.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_fri_stir.pdf -------------------------------------------------------------------------------- /notes_hypernova.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_hypernova.pdf -------------------------------------------------------------------------------- /notes_spartan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_spartan.pdf -------------------------------------------------------------------------------- /sigma-or-notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/sigma-or-notes.pdf -------------------------------------------------------------------------------- /seminarexercises.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/seminarexercises.pdf -------------------------------------------------------------------------------- /galois-theory-notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/galois-theory-notes.pdf -------------------------------------------------------------------------------- /notes_reed-solomon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/notes_reed-solomon.pdf -------------------------------------------------------------------------------- /slides_hypernova-part1-introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/slides_hypernova-part1-introduction.pdf -------------------------------------------------------------------------------- /abstract-algebra-charles-pinter-notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/abstract-algebra-charles-pinter-notes.pdf -------------------------------------------------------------------------------- /slides_hypernova-part2-multifolding-unfolded.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnaucube/math/HEAD/slides_hypernova-part2-multifolding-unfolded.pdf -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | # usage: 2 | # install `typos`: https://github.com/crate-ci/typos 3 | # run: typos --config typos.toml 4 | 5 | [default.extend-words] 6 | groth = "groth" 7 | pinter = "pinter" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sage.py 2 | *.aux 3 | *.fdb_latexmk 4 | *.fls 5 | *.log 6 | *.out 7 | *.synctex.gz 8 | *.toc 9 | *.bbl 10 | *.blg 11 | *.nav 12 | *.snm 13 | *.vrb 14 | galois-theory-notes.bib 15 | -------------------------------------------------------------------------------- /fft-notes.bib: -------------------------------------------------------------------------------- 1 | @misc{gstrang, 2 | title = {Linear Algebra and Its Applications, by Gilbert Strang (chapter 3.5)}, 3 | howpublished = "\url{https://archive.org/details/linearalgebrait00stra}", 4 | } 5 | @misc{tpornin, 6 | title = {{Thomas Pornin} mathoverflow answer}, 7 | howpublished = "\url{https://crypto.stackexchange.com/a/63616}", 8 | } 9 | @misc{rfateman, 10 | title = {notes by {Prof. R. Fateman}}, 11 | howpublished = "\url{https://www.csee.umbc.edu/~phatak/691a/fft-lnotes/fftnotes.pdf}", 12 | } 13 | @misc{fftrs, 14 | title = {fft-rs}, 15 | howpublished = "\url{https://github.com/arnaucube/fft-rs}", 16 | } 17 | @misc{fftsage, 18 | title = {fft-sage}, 19 | howpublished = "\url{https://github.com/arnaucube/math/blob/master/fft.sage}", 20 | } 21 | -------------------------------------------------------------------------------- /notes_reed-solomon.bib: -------------------------------------------------------------------------------- 1 | @misc{reedsolomon, 2 | title = {{Reed-Solomon Codes paper}}, 3 | author = {I. S. Reed, G. Solomon}, 4 | howpublished = "\url{https://faculty.math.illinois.edu/~duursma/CT/RS-1960.pdf}", 5 | } 6 | 7 | @misc{reedsolomon-bruce, 8 | title = {Reed-Solomon}, 9 | author = {Bruce Maggs}, 10 | howpublished = "\url{https://courses.cs.duke.edu/spring10/cps296.3/rs_scribe.pdf}", 11 | } 12 | 13 | @misc{vanddetproof, 14 | title = {Vandermonde determinant proof}, 15 | author = {Pedro Tamaroff}, 16 | howpublished = "\url{https://math.stackexchange.com/questions/525334/vandermonde-determinant-by-induction}", 17 | } 18 | 19 | @misc{vanddetproof2, 20 | title = {Abstract Algebra, Theory and Applications. \emph{sec.22.2}}, 21 | author = {Thomas W. Judson}, 22 | } 23 | 24 | @misc{cyclotomicpolyroots, 25 | title = {Lagrange bases in subgroups of Fp*: a hands-on introduction}, 26 | author = {Alex Kampa}, 27 | howpublished = "\url{https://github.com/aragon/research/blob/main/blog/001_Lagrange/lagrange.pdf}", 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # math/cryptography 2 | 3 | Notes, code and documents done while reading books and papers. 4 | 5 | ## mathematics 6 | 7 | - [Notes on "Abstract Algebra" book, by Charles C. Pinter](abstract-algebra-charles-pinter-notes.pdf) 8 | - [Notes on Weil pairing](weil-pairing.pdf) 9 | - [Notes on Galois Theory](galois-theory-notes.pdf) 10 | 11 | 12 | In-between math & crypto: 13 | 14 | - [Notes on the DFT & FFT](fft-notes.pdf) 15 | - [Notes on NTT](notes_ntt.pdf) 16 | - [Notes on Reed-Solomon codes](notes_reed-solomon.pdf) 17 | 18 | ## cryptography 19 | 20 | - [Notes on Caulk & Caulk+ papers](notes_caulk.pdf) 21 | - [Notes on the BLS signatures](notes_bls-sig.pdf) 22 | - [Notes on IPA from Halo paper](notes_halo.pdf) 23 | - [Notes on Sonic paper](notes_sonic.pdf) 24 | - [Notes on Sigma protocol and OR proofs](sigma-or-notes.pdf) 25 | - [Notes on FRI and STIR](notes_fri_stir.pdf) 26 | - [Notes on Spartan](notes_spartan.pdf) 27 | - [Notes on Nova](notes_nova.pdf) 28 | - [Notes on HyperNova](notes_hypernova.pdf) 29 | 30 | ## code 31 | Also some Sage implementations can be found in the `*.sage` files of this repo. 32 | Also some of the algorithms and schemes can be found implemented (mostly in Rust language) in various repositories of the github https://github.com/arnaucube . 33 | -------------------------------------------------------------------------------- /bls-sigs.sage: -------------------------------------------------------------------------------- 1 | # toy implementation of BLS signatures 2 | 3 | load("bls12-381.sage") 4 | from hashlib import sha256 5 | 6 | def hash(m): 7 | h_output = sha256(str(m).encode('utf-8')) 8 | return int(h_output.hexdigest(), 16) 9 | 10 | def hash_to_point(m): 11 | # WARNING this hash-to-point approach should not be used! 12 | h = hash(m) 13 | return e.G2 * h 14 | 15 | 16 | e = Pairing() 17 | 18 | class Signer: 19 | def __init__(self): 20 | self.sk = e.F1.random_element() 21 | self.pk = self.sk * e.G1 22 | 23 | def sign(self, m): 24 | H = hash_to_point(m) 25 | return self.sk * H 26 | 27 | def verify(pk, s, m): 28 | H = hash_to_point(m) 29 | return e.pair(e.G1, s) == e.pair(pk, H) 30 | 31 | def aggr(points): 32 | R = 0 33 | for i in range(len(points)): 34 | R = R + points[i] 35 | return R 36 | 37 | 38 | m = 1234 39 | 40 | # single signature & verification 41 | user0 = Signer() 42 | s = user0.sign(m) 43 | v = verify(user0.pk, s, m) 44 | assert v 45 | 46 | 47 | # BLS signature aggregation 48 | n = 10 49 | users = [None]*n 50 | pks = [None]*n 51 | sigs = [None]*n 52 | for i in range(n): 53 | users[i] = Signer() 54 | pks[i] = users[i].pk 55 | sigs[i] = users[i].sign(m) 56 | 57 | # aggregate sigs & pks 58 | s_aggr = aggr(sigs) 59 | pk_aggr = aggr(pks) 60 | 61 | # verify aggregated signature 62 | v = verify(pk_aggr, s_aggr, m) 63 | assert v 64 | -------------------------------------------------------------------------------- /kzg.sage: -------------------------------------------------------------------------------- 1 | # toy implementation of BLS signatures in Sage 2 | # 3 | # Scheme overview: https://arnaucube.com/blog/kzg-commitments.html 4 | # Go implementation: https://github.com/arnaucube/kzg-commitments-study 5 | 6 | load("bls12-381.sage") 7 | 8 | e = Pairing() 9 | 10 | def new_ts(l): 11 | Fr = GF(e.r) 12 | s = Fr.random_element() 13 | print("s", s) 14 | tauG1 = [None] * l 15 | tauG2 = [None] * l 16 | for i in range(0, l): # TODO probably duplicate G1 & G2 instead of first powering s^i and then * G_j 17 | sPow = Integer(s)^i 18 | tauG1[i] = sPow * e.G1 19 | tauG2[i] = sPow * e.G2 20 | 21 | return (tauG1, tauG2) 22 | 23 | def commit(taus, p): 24 | return evaluate_at_tau(p, taus) 25 | 26 | # evaluates p at tau 27 | def evaluate_at_tau(p, taus): 28 | e = 0 29 | for i in range(0, len(p.list())): 30 | e = e + p[i] * taus[i] 31 | return e 32 | 33 | def evaluation_proof(tau, p, z, y): 34 | # (p - y) 35 | n = p - y 36 | # (t - z) 37 | d = (t-z) 38 | # q, rem = n / d 39 | q = n / d 40 | print("q", q) 41 | q = q.numerator() 42 | den = q.denominator() 43 | print("q", q) 44 | print("den", den) 45 | # check that den = 1 46 | assert(den==1) # rem=0 47 | # proof: e = [q(t)]₁ 48 | return evaluate_at_tau(q, tau) 49 | 50 | def verify(tau, c, proof, z, y): 51 | # [t]₂ - [z]₂ 52 | sz = tau[1] - z*e.G2 53 | 54 | # c - [y]₁ 55 | cy = c - y*e.G1 56 | 57 | print("proof", proof) 58 | print("sz", sz) 59 | print("cy", cy) 60 | lhs = e.pair(proof, sz) 61 | rhs = e.pair(cy, e.G2) 62 | print("lhs", lhs) 63 | print("rhs", rhs) 64 | return lhs == rhs 65 | 66 | 67 | (tauG1, tauG2) = new_ts(5) 68 | 69 | R. = PolynomialRing(e.F1) 70 | p = t^3 + t + 5 71 | 72 | c = commit(tauG1, p) 73 | 74 | z = 3 75 | y = p(z) # = 35 76 | 77 | proof = evaluation_proof(tauG1, p, z, y) 78 | print("proof", proof) 79 | 80 | v = verify(tauG2, c, proof, z, y) 81 | print(v) 82 | assert(v) 83 | -------------------------------------------------------------------------------- /blind-sign-over-ec.sage: -------------------------------------------------------------------------------- 1 | # Implementation of: https://sci-hub.se/10.1109/iccke.2013.6682844 2 | # more details at: https://arnaucube.com/blog/blind-signatures-ec.html#the-scheme 3 | # A Go implementation of this schema can be found at: https://github.com/arnaucube/go-blindsecp256k1 4 | 5 | from hashlib import sha256 6 | 7 | def hash(m): 8 | h_output = sha256(str(m).encode('utf-8')) 9 | return int(h_output.hexdigest(), 16) 10 | 11 | 12 | 13 | class User: 14 | def __init__(self, F, G): 15 | self.F = F # Z_q 16 | self.G = G # elliptic curve generator 17 | 18 | def blind_msg(self, m, R_): 19 | self.a = self.F.random_element() 20 | self.b = self.F.random_element() 21 | self.R = self.a * R_ + self.b * self.G 22 | m_ = self.F(self.a)^(-1) * self.F(self.R.xy()[0]) * self.F(hash(m)) 23 | return m_ 24 | 25 | def unblind_sig(self, s_): 26 | s = self.a * s_ + self.b 27 | return (self.R, s) 28 | 29 | 30 | class Signer: 31 | def __init__(self, F, G): 32 | self.F = F # Z_q 33 | self.G = G # elliptic curve generator 34 | 35 | # gen Signer's key pair 36 | self.d = self.F.random_element() 37 | self.Q = self.G * self.d 38 | 39 | 40 | def new_request_params(self): 41 | self.k = self.F.random_element() 42 | R_ = self.G * self.k 43 | return R_ 44 | 45 | def blind_sign(self, m_): 46 | return self.d * m_ + self.k 47 | 48 | def verify(G, Q, sig, m): 49 | (R, s) = sig 50 | return s*G == R + (Fq(R.xy()[0]) * Fq(hash(m))) * Q 51 | 52 | 53 | 54 | # ethereum elliptic curve 55 | p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F # base field 56 | a = 0 57 | b = 7 58 | F = GF(p) # base field 59 | E = EllipticCurve(F, [a,b]) 60 | GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 61 | GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 62 | g = E(GX,GY) 63 | n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 64 | q = g.order() # order of Fp 65 | assert is_prime(p) 66 | assert is_prime(q) 67 | Fq = GF(q) # scalar field 68 | 69 | 70 | 71 | # protocol flow: 72 | 73 | user = User(Fq, g) 74 | signer = Signer(Fq, g) 75 | 76 | R_ = signer.new_request_params() 77 | 78 | m = 12345 # user's message 79 | m_ = user.blind_msg(m, R_) 80 | 81 | s_ = signer.blind_sign(m_) 82 | 83 | sig = user.unblind_sig(s_) 84 | 85 | v = verify(g, signer.Q, sig, m) 86 | print(v) 87 | assert v 88 | -------------------------------------------------------------------------------- /number-theory.sage: -------------------------------------------------------------------------------- 1 | # Chinese Remainder Theorem 2 | def crt(a_i, m_i): 3 | if len(a_i)!=len(m_i): 4 | raise Exception("error, a_i and m_i must be of the same length") 5 | 6 | M=1 7 | for i in range(len(m_i)): 8 | M = M * m_i[i] 9 | 10 | x = 0 11 | for i in range(len(a_i)): 12 | M_i = M/m_i[i] 13 | y_i = Integer(mod(M_i^-1, m_i[i])) 14 | x = x + a_i[i] * M_i * y_i 15 | return mod(x, M) 16 | 17 | # gcd, using Binary Euclidean algorithm 18 | def gcd(a, b): 19 | g=1 20 | # random_elementove powers of two from the gcd 21 | while mod(a, 2)==0 and mod(b, 2)==0: 22 | a=a/2 23 | b=b/2 24 | g=2*g 25 | # at least one of a and b is now odd 26 | while a!=0: 27 | while mod(a, 2)==0: 28 | a=a/2 29 | while mod(b, 2)==0: 30 | b=b/2 31 | # now both a and b are odd 32 | if a>=b: 33 | a = (a-b)/2 34 | else: 35 | b = (b-a)/2 36 | 37 | return g*b 38 | 39 | def gcd_recursive(a, b): 40 | if mod(a, b)==0: 41 | return b 42 | return gcd_recursive(b, mod(a,b)) 43 | 44 | 45 | # Extended Euclidean algorithm 46 | # Inputs: a, b 47 | # Outputs: r, x, y, such that r = gcd(a, b) = x*a + y*b 48 | def egcd(a, b): 49 | s=0 50 | s_=1 51 | t=1 52 | t_=0 53 | r=b 54 | r_=a 55 | while r!=0: 56 | q = r_ // r 57 | (r_,r) = (r,r_ - q*r) 58 | (s_,s) = (s,s_ - q*s) 59 | (t_,t) = (t,t_ - q*t) 60 | 61 | d = r_ 62 | x = s_ 63 | y = t_ 64 | 65 | return d, x, y 66 | 67 | def egcd_recursive(a, b): 68 | if b==0: 69 | return a, 1, 0 70 | 71 | g, x, y = egcd_recursive(b, a%b) 72 | return g, y, x - (a//b) * y 73 | 74 | # Inverse modulo N, using the Extended Euclidean algorithm 75 | def inv_mod(a, N): 76 | g, x, y = egcd(a, N) 77 | if g != 1: 78 | raise Exception("inv_mod err, g!=1") 79 | return mod(x, N) 80 | 81 | # Tests 82 | 83 | ##### 84 | # Chinese Remainder Theorem tests 85 | a_i = [5, 3, 10] 86 | m_i = [7, 11, 13] 87 | assert crt(a_i, m_i) == 894 88 | 89 | a_i = [3, 8] 90 | m_i = [13, 17] 91 | assert crt(a_i, m_i) == 42 92 | 93 | 94 | ##### 95 | # gcd, using Binary Euclidean algorithm tests 96 | assert gcd(21, 12) == 3 97 | assert gcd(1_426_668_559_730, 810_653_094_756) == 1_417_082 98 | 99 | assert gcd_recursive(21, 12) == 3 100 | 101 | ##### 102 | # Extended Euclidean algorithm tests 103 | assert egcd(7, 19) == (1, -8, 3) 104 | assert egcd_recursive(7, 19) == (1, -8, 3) 105 | 106 | ##### 107 | # Inverse modulo N tests 108 | assert inv_mod(7, 19) == 11 109 | -------------------------------------------------------------------------------- /powersoftau.sage: -------------------------------------------------------------------------------- 1 | # Sage impl of the powers of tau, 2 | # a Go implementation can be found at: https://github.com/arnaucube/eth-kzg-ceremony-alt 3 | 4 | 5 | load("bls12-381.sage") # file from https://github.com/arnaucube/math/blob/master/bls12-381.sage 6 | e = Pairing() 7 | 8 | def new_empty_SRS(nG1, nG2): 9 | g1s = [None] * nG1 10 | g2s = [None] * nG2 11 | for i in range(0, nG1): 12 | g1s[i] = e.G1 13 | for i in range(0, nG2): 14 | g2s[i] = e.G2 15 | 16 | return [g1s, g2s] 17 | 18 | def new_tau(random): 19 | return e.F1(random) 20 | 21 | def compute_contribution(new_tau, prev_srs): 22 | g1s = [None] * len(prev_srs[0]) 23 | g2s = [None] * len(prev_srs[1]) 24 | srs = [g1s, g2s] 25 | Q = e.r 26 | 27 | # compute [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁, where n = len(prev_srs.G1s) 28 | for i in range(0, len(prev_srs[0])): 29 | srs[0][i] = (new_tau^i) * prev_srs[0][i] 30 | 31 | # compute [τ'⁰]₂, [τ'¹]₂, [τ'²]₂, ..., [τ'ⁿ⁻¹]₂, where n = len(prev_srs.G2s) 32 | for i in range(0, len(prev_srs[1])): 33 | srs[1][i] = (new_tau^i) * prev_srs[1][i] 34 | 35 | return srs 36 | 37 | def generate_proof(tau, prev_srs, new_srs): 38 | # g_1^{tau'} = g_1^{p * tau} = SRS_G1s[1] * p 39 | g1_ptau = prev_srs[0][1] * tau 40 | # g_2^{p} 41 | g2_p = tau * e.G2 42 | return [g1_ptau, g2_p] 43 | 44 | def verify(prev_srs, new_srs, proof): 45 | # 1. check that elements of the newSRS are valid points 46 | for i in range(0, len(new_srs[0])-1): 47 | assert new_srs[0][i] != None 48 | assert new_srs[0][i] != e.E1(0) 49 | assert new_srs[0][i] in e.E1 50 | 51 | for i in range(0, len(new_srs[1])-1): 52 | assert new_srs[1][i] != None 53 | assert new_srs[1][i] != e.E2(0) 54 | assert new_srs[1][i] in e.E2 55 | 56 | # 2. check proof.G1PTau == newSRS.G1Powers[1] 57 | assert proof[0] == new_srs[0][1] 58 | 59 | # 3. check newSRS.G1s[1] (g₁^τ'), is correctly related to prev_srs.G1s[1] (g₁^τ) 60 | # e([τ]₁, [p]₂) == e([τ']₁, [1]₂) 61 | assert e.pair(prev_srs[0][1], proof[1]) == e.pair(new_srs[0][1], e.G2) 62 | 63 | # 4. check newSRS following the powers of tau structure 64 | # i) e([τ'ⁱ]₁, [τ']₂) == e([τ'ⁱ⁺¹]₁, [1]₂), for i ∈ [1, n−1] 65 | for i in range(0, len(new_srs[0])-1): 66 | assert e.pair(new_srs[0][i], new_srs[1][1]) == e.pair(new_srs[0][i+1], e.G2) 67 | 68 | # ii) e([τ']₁, [τ'ʲ]₂) == e([1]₁, [τ'ʲ⁺¹]₂), for j ∈ [1, m−1] 69 | for i in range(0, len(new_srs[1])-1): 70 | assert e.pair(new_srs[0][1], new_srs[1][i]) == e.pair(e.G1, new_srs[1][i+1]) 71 | 72 | 73 | 74 | 75 | 76 | (prev_srs) = new_empty_SRS(5, 3) 77 | 78 | random = 12345 79 | tau = new_tau(random) 80 | 81 | new_srs = compute_contribution(tau, prev_srs) 82 | 83 | proof = generate_proof(tau, prev_srs, new_srs) 84 | 85 | verify(prev_srs, new_srs, proof) 86 | -------------------------------------------------------------------------------- /ccs-r1cs.sage: -------------------------------------------------------------------------------- 1 | # R1CS-to-CCS (https://eprint.iacr.org/2023/552) Sage prototype 2 | 3 | # utils 4 | def matrix_vector_product(M, v): 5 | n = M.nrows() 6 | r = [F(0)] * n 7 | for i in range(0, n): 8 | for j in range(0, M.ncols()): 9 | r[i] += M[i][j] * v[j] 10 | return r 11 | def hadamard_product(a, b): 12 | n = len(a) 13 | r = [None] * n 14 | for i in range(0, n): 15 | r[i] = a[i] * b[i] 16 | return r 17 | def vec_add(a, b): 18 | n = len(a) 19 | r = [None] * n 20 | for i in range(0, n): 21 | r[i] = a[i] + b[i] 22 | return r 23 | def vec_elem_mul(a, s): 24 | r = [None] * len(a) 25 | for i in range(0, len(a)): 26 | r[i] = a[i] * s 27 | return r 28 | # end of utils 29 | 30 | 31 | # can use any finite field, using a small one for the example 32 | F = GF(101) 33 | 34 | # R1CS matrices for: x^3 + x + 5 = y (example from article 35 | # https://www.vitalik.ca/general/2016/12/10/qap.html ) 36 | A = matrix([ 37 | [F(0), 1, 0, 0, 0, 0], 38 | [0, 0, 0, 1, 0, 0], 39 | [0, 1, 0, 0, 1, 0], 40 | [5, 0, 0, 0, 0, 1], 41 | ]) 42 | B = matrix([ 43 | [F(0), 1, 0, 0, 0, 0], 44 | [0, 1, 0, 0, 0, 0], 45 | [1, 0, 0, 0, 0, 0], 46 | [1, 0, 0, 0, 0, 0], 47 | ]) 48 | C = matrix([ 49 | [F(0), 0, 0, 1, 0, 0], 50 | [0, 0, 0, 0, 1, 0], 51 | [0, 0, 0, 0, 0, 1], 52 | [0, 0, 1, 0, 0, 0], 53 | ]) 54 | 55 | print("R1CS matrices:") 56 | print("A:", A) 57 | print("B:", B) 58 | print("C:", C) 59 | 60 | 61 | z = [F(1), 3, 35, 9, 27, 30] 62 | print("z:", z) 63 | 64 | assert len(z) == A.ncols() 65 | 66 | n = A.ncols() # == len(z) 67 | m = A.nrows() 68 | # l = len(io) # not used for now 69 | 70 | # check R1CS relation 71 | Az = matrix_vector_product(A, z) 72 | Bz = matrix_vector_product(B, z) 73 | Cz = matrix_vector_product(C, z) 74 | print("\nR1CS relation check (Az ∘ Bz == Cz):", hadamard_product(Az, Bz) == Cz) 75 | assert hadamard_product(Az, Bz) == Cz 76 | 77 | 78 | # Translate R1CS into CCS: 79 | print("\ntranslate R1CS into CCS:") 80 | 81 | # fixed parameters (and n, m, l are direct from R1CS) 82 | t=3 83 | q=2 84 | d=2 85 | S1=[0,1] 86 | S2=[2] 87 | S = [S1, S2] 88 | c0=1 89 | c1=-1 90 | c = [c0, c1] 91 | 92 | M = [A, B, C] 93 | 94 | print("CCS values:") 95 | print("n: %s, m: %s, t: %s, q: %s, d: %s" % (n, m, t, q, d)) 96 | print("M:", M) 97 | print("z:", z) 98 | print("S:", S) 99 | print("c:", c) 100 | 101 | # check CCS relation (this is agnostic to R1CS, for any CCS instance) 102 | r = [F(0)] * m 103 | for i in range(0, q): 104 | hadamard_output = [F(1)]*m 105 | for j in S[i]: 106 | hadamard_output = hadamard_product(hadamard_output, 107 | matrix_vector_product(M[j], z)) 108 | 109 | r = vec_add(r, vec_elem_mul(hadamard_output, c[i])) 110 | 111 | print("\nCCS relation check (∑ cᵢ ⋅ ◯ Mⱼ z == 0):", r == [0]*m) 112 | assert r == [0]*m 113 | -------------------------------------------------------------------------------- /notes_bls-sig.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{enumerate} 7 | \usepackage{hyperref} 8 | \hypersetup{ 9 | colorlinks, 10 | citecolor=black, 11 | filecolor=black, 12 | linkcolor=black, 13 | urlcolor=blue 14 | } 15 | \usepackage{xcolor} 16 | 17 | % prevent warnings of underfull \hbox: 18 | \usepackage{etoolbox} 19 | \apptocmd{\sloppy}{\hbadness 4000\relax}{}{} 20 | 21 | \theoremstyle{definition} 22 | \newtheorem{definition}{Def}[section] 23 | \newtheorem{theorem}[definition]{Thm} 24 | 25 | 26 | \title{Notes on BLS Signatures} 27 | \author{arnaucube} 28 | \date{July 2022} 29 | 30 | \begin{document} 31 | 32 | \maketitle 33 | 34 | \begin{abstract} 35 | Notes taken while reading about BLS signatures \cite{bls-sig-eth2}. Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 36 | 37 | The notes are not complete, don't include all the steps neither all the proofs. 38 | \end{abstract} 39 | 40 | % \tableofcontents 41 | 42 | \section{BLS signatures} 43 | 44 | \paragraph{Key generation} 45 | $sk \in \mathbb{Z}_q$, $pk = [sk] \cdot g_1$, where $g_1 \in G_1$, and is the generator. 46 | 47 | \paragraph{Signature} 48 | $$\sigma = [sk] \cdot H(m)$$ 49 | where $H$ is a function that maps to a point in $G_2$. So $H(m), \sigma \in G_2$. 50 | 51 | \paragraph{Verification} 52 | $$e(g_1, \sigma) == e(pk, H(m))$$ 53 | 54 | Unfold: 55 | $$e(pk, H(m)) = e([sk] \cdot g_1, H(m) = e(g_1, H(m))^{sk} = e(g_1, [sk] \cdot H(m)) = e(g_1, \sigma))$$ 56 | 57 | \paragraph{Aggregation} 58 | Signatures aggregation: 59 | $$\sigma_{aggr} = \sigma_1 + \sigma_2 + \ldots + \sigma_n$$ 60 | where $\sigma_{aggr} \in G_2$, and an aggregated signatures is indistinguishable from a non-aggregated signature. 61 | 62 | \vspace{0.5cm} 63 | Public keys aggregation: 64 | $$pk_{aggr} = pk_1 + pk_2 + \ldots + pk_n$$ 65 | where $pk_{aggr} \in G_1$, and an aggregated public keys is indistinguishable from a non-aggregated public key. 66 | 67 | 68 | \paragraph{Verification of aggregated signatures} 69 | Identical to verification of a normal signature as long as we use the same corresponding aggregated public key: 70 | $$e(g_1, \sigma_{aggr})==e(pk_{aggr}, H(m))$$ 71 | 72 | Unfold: 73 | $$e(pk_{aggr}, H(m))= e(pk_1 + pk_2 + \ldots + pk_n, H(m)) =$$ 74 | $$=e([sk_1] \cdot g_1 + [sk_2] \cdot g_1 + \ldots + [sk_n] \cdot g_1, H(m))=$$ 75 | $$=e([sk_1 + sk_2 + \ldots + sk_n] \cdot g_1, H(m))=$$ 76 | $$=[sk_1 + sk_2 + \ldots + sk_n]~\cdot~e(g_1, H(m))=$$ 77 | $$=e(g_1, [sk_1 + sk_2 + \ldots + sk_n] \cdot H(m))=$$ 78 | $$=e(g_1, [sk_1] \cdot H(m) + [sk_2] \cdot H(m) + \ldots + [sk_n] \cdot H(m))=$$ 79 | $$=e(g_1, \sigma_1 + \sigma_2 + \ldots + \sigma_n)= e(g_1, \sigma_{aggr})$$ 80 | 81 | 82 | Note: in the current notes $pk \in G_1$ and $\sigma, H(m) \in G_2$, but we could use $\sigma, H(m) \in G_1$ and $pk \in G_2$. 83 | 84 | \bibliography{paper-notes.bib} 85 | \bibliographystyle{unsrt} 86 | 87 | \end{document} 88 | -------------------------------------------------------------------------------- /bls12-381.sage: -------------------------------------------------------------------------------- 1 | # The code of this file has been adapted from: 2 | # https://github.com/osirislab/CSAW-CTF-2021-Finals/blob/main/crypto/aBoLiSh_taBLeS/chal.sage 3 | # 4 | # ## Example of usage: 5 | # load("bls12-381.sage") 6 | # e = Pairing() 7 | # assert e.pair(e.G1 * 3, e.G2 * 2) == e.pair(e.G1, e.G2)^6 8 | 9 | 10 | class Pairing(): 11 | def __init__(self): 12 | # BLS12-381 Parameters 13 | # https://github.com/zkcrypto/bls12_381 14 | self.p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab 15 | self.r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 16 | self.h1 = 0x396c8c005555e1568c00aaab0000aaab 17 | self.h2 = 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 18 | 19 | # Define base fields 20 | self.F1 = GF(self.p) 21 | F2. = GF(self.p^2, x, x^2 + 1) 22 | self.u = u 23 | self.F2 = F2 24 | F12. = GF(self.p^12, x, x^12 - 2*x^6 + 2) 25 | self.w = w 26 | self.F12 = F12 27 | 28 | # Define the Elliptic Curves 29 | self.E1 = EllipticCurve(self.F1, [0, 4]) 30 | self.E2 = EllipticCurve(F2, [0, 4*(1 + self.u)]) 31 | self.E12 = EllipticCurve(F12, [0, 4]) 32 | 33 | # Generator of order r in E1 / F1 34 | G1x = 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb 35 | G1y = 0x8b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 36 | self.G1 = self.E1(G1x, G1y) 37 | 38 | # Generator of order r in E2 / F2 39 | G2x0 = 0x24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 40 | G2x1 = 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e 41 | G2y0 = 0xce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801 42 | G2y1 = 0x606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be 43 | self.G2 = self.E2(G2x0 + self.u*G2x1, G2y0 + self.u*G2y1) 44 | 45 | 46 | def lift_E1_to_E12(self, P): 47 | """ 48 | Lift point on E/F_q to E/F_{q^12} using the natural lift 49 | """ 50 | assert P.curve() == self.E1, "Attempting to lift a point from the wrong curve." 51 | return self.E12(P) 52 | 53 | def lift_E2_to_E12(self, P): 54 | """ 55 | Lift point on E/F_{q^2} to E/F_{q_12} through the sextic twist 56 | """ 57 | assert P.curve() == self.E2, "Attempting to lift a point from the wrong curve." 58 | xs, ys = [c.polynomial().coefficients() for c in (self.h2*P).xy()] 59 | nx = self.F12(xs[0] - xs[1] + self.w ^ 6*xs[1]) 60 | ny = self.F12(ys[0] - ys[1] + self.w ^ 6*ys[1]) 61 | return self.E12(nx / (self.w ^ 2), ny / (self.w ^ 3)) 62 | 63 | def pair(self, A, B): 64 | A = self.lift_E1_to_E12(A) 65 | B = self.lift_E2_to_E12(B) 66 | return A.ate_pairing(B, self.r, 12, self.E12.trace_of_frobenius()) 67 | 68 | -------------------------------------------------------------------------------- /ccs-plonk.sage: -------------------------------------------------------------------------------- 1 | # Plonk-CCS (https://eprint.iacr.org/2023/552) Sage prototype 2 | 3 | # utils 4 | def matrix_vector_product(M, v): 5 | n = M.nrows() 6 | r = [F(0)] * n 7 | for i in range(0, n): 8 | for j in range(0, M.ncols()): 9 | r[i] += M[i][j] * v[j] 10 | return r 11 | def hadamard_product(a, b): 12 | n = len(a) 13 | r = [None] * n 14 | for i in range(0, n): 15 | r[i] = a[i] * b[i] 16 | return r 17 | def vec_add(a, b): 18 | n = len(a) 19 | r = [None] * n 20 | for i in range(0, n): 21 | r[i] = a[i] + b[i] 22 | return r 23 | def vec_elem_mul(a, s): 24 | r = [None] * len(a) 25 | for i in range(0, len(a)): 26 | r[i] = a[i] * s 27 | return r 28 | # end of utils 29 | 30 | 31 | # can use any finite field, using a small one for the example 32 | F = GF(101) 33 | # F = GF(21888242871839275222246405745257275088696311157297823662689037894645226208583) 34 | 35 | 36 | # The following CCS instance values have been provided by Carlos 37 | # (https://github.com/CPerezz) and Edu (https://github.com/ed255), 38 | # and this sage script was made to check the CCS relation. 39 | 40 | ## Checks performed by this Plonk/CCS instance: 41 | # - binary check for x0, x1 42 | # - 2*x2 + 2*x3 == x4 43 | M0 = matrix([ 44 | [F(0), 1, 0, 0, 0, 0, 0], 45 | [0, 0, 1, 0, 0, 0, 0], 46 | [0, 0, 0, 1, 0, 0, 0], 47 | [0, 0, 0, 0, 0, 0, 1], 48 | ]) 49 | M1 = matrix([ 50 | [F(0), 1, 0, 0, 0, 0, 0], 51 | [0, 0, 1, 0, 0, 0, 0], 52 | [0, 0, 0, 0, 1, 0, 0], 53 | [0, 0, 0, 0, 0, 0, 1], 54 | ]) 55 | M2 = matrix([ 56 | [F(0), 1, 0, 0, 0, 0, 0], 57 | [0, 0, 1, 0, 0, 0, 0], 58 | [0, 0, 0, 0, 0, 1, 0], 59 | [0, 0, 0, 0, 0, 0, 1], 60 | ]) 61 | M3 = matrix([ 62 | [F(1), 0, 0, 0, 0, 0, 0], 63 | [1, 0, 0, 0, 0, 0, 0], 64 | [0, 0, 0, 0, 0, 0, 0], 65 | [0, 0, 0, 0, 0, 0, 0], 66 | ]) 67 | M4 = matrix([ 68 | [F(0), 0, 0, 0, 0, 0, 0], 69 | [0, 0, 0, 0, 0, 0, 0], 70 | [2, 0, 0, 0, 0, 0, 0], 71 | [0, 0, 0, 0, 0, 0, 0], 72 | ]) 73 | M5 = matrix([ 74 | [F(0), 0, 0, 0, 0, 0, 0], 75 | [0, 0, 0, 0, 0, 0, 0], 76 | [2, 0, 0, 0, 0, 0, 0], 77 | [0, 0, 0, 0, 0, 0, 0], 78 | ]) 79 | M6 = matrix([ 80 | [F(-1), 0, 0, 0, 0, 0, 0], 81 | [-1, 0, 0, 0, 0, 0, 0], 82 | [-1, 0, 0, 0, 0, 0, 0], 83 | [0, 0, 0, 0, 0, 0, 0], 84 | ]) 85 | M7 = matrix([ 86 | [F(0), 0, 0, 0, 0, 0, 0], 87 | [0, 0, 0, 0, 0, 0, 0], 88 | [0, 0, 0, 0, 0, 0, 0], 89 | [0, 0, 0, 0, 0, 0, 0], 90 | ]) 91 | 92 | z = [F(1), 0, 1, 2, 3, 10, 42] 93 | 94 | print("z:", z) 95 | 96 | assert len(z) == M0.ncols() 97 | 98 | # CCS parameters 99 | n = M0.ncols() # == len(z) 100 | m = M0.nrows() 101 | t=8 102 | q=5 103 | d=3 104 | S = [[3,0,1], [4,0], [5,1], [6,2], [7]] 105 | c = [1, 1, 1, 1, 1] 106 | 107 | M = [M0,M1,M2,M3,M4,M5,M6,M7] 108 | 109 | print("CCS values:") 110 | print("n: %s, m: %s, t: %s, q: %s, d: %s" % (n, m, t, q, d)) 111 | print("M:", M) 112 | print("z:", z) 113 | print("S:", S) 114 | print("c:", c) 115 | 116 | # check CCS relation (this is agnostic to Plonk, for any CCS instance) 117 | r = [F(0)] * m 118 | for i in range(0, q): 119 | hadamard_output = [F(1)]*m 120 | for j in S[i]: 121 | hadamard_output = hadamard_product(hadamard_output, 122 | matrix_vector_product(M[j], z)) 123 | 124 | r = vec_add(r, vec_elem_mul(hadamard_output, c[i])) 125 | 126 | print("\nCCS relation check (∑ cᵢ ⋅ ◯ Mⱼ z == 0):", r == [0]*m) 127 | assert r == [0]*m 128 | -------------------------------------------------------------------------------- /seminarexercises.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{enumerate} 7 | \usepackage{hyperref} 8 | \hypersetup{ 9 | colorlinks, 10 | citecolor=black, 11 | filecolor=black, 12 | linkcolor=black, 13 | urlcolor=black 14 | } 15 | 16 | % custom solution environment to set custom numbers 17 | \theoremstyle{definition} 18 | \newtheorem{innersolution}{Solution} 19 | \newenvironment{solution}[1] 20 | {\renewcommand\theinnersolution{#1}\innersolution} 21 | {\endinnersolution} 22 | 23 | \title{Seminar exercises} 24 | \author{ } 25 | \date{February 2022} 26 | 27 | \begin{document} 28 | \maketitle 29 | 30 | \begin{solution}{1.9}\ 31 | \begin{enumerate}[1.] 32 | \item Let $f(a) = u$, then $g(f(a)) = g(u)$, so $g \circ f$ is a function. 33 | \item We can see that composition of functions is associative as follows:\\ 34 | we know that $[ f \circ g](x) = f(g(x)), \forall x \in A$,\\ 35 | so, 36 | $$(h \circ [g \circ f])(x) = h([g \circ f](x)) = h(g(f(x)))$$ 37 | \\ 38 | and 39 | $$([h \circ g] \circ f)(x) = [h \circ g](f(x)) = h(g(f(x)))$$ 40 | Then, we can see that $$h \circ (g \circ f) = h(g(f(x))) = (h \circ g) \circ f$$ 41 | \end{enumerate} 42 | \end{solution} 43 | 44 | \begin{solution}{1.28}\ 45 | \emph{(WIP)}\\ 46 | It is isomorphic to the cosets of the \emph{nth} roots of unity, which are $\mathbb{G}_n = \{w_k\}^{n-1}_{k=0}$, where $w_k=e^{\frac{2 \pi i}{n}}$. 47 | \end{solution} 48 | 49 | \begin{solution}{2.2}\ 50 | 51 | To prove that the inverse $x^{-1}$ is unique, assume $x^{-1}$ and $\tilde{x}^{-1}$ are two inverses of $x$.\\ 52 | By the definition of the inverse, we know that $x \cdot x^{-1} = e$. And by the definition of the unit element, we know that $x \cdot e = x$.\\ 53 | Then, $$x^{-1} \cdot (x \cdot \tilde{x}^{-1}) = x^{-1} \cdot e = x^{-1}$$ 54 | and $$(x^{-1} \cdot x) \cdot \tilde{x}^{-1} = e \cdot \tilde{x}^{-1} = \tilde{x}^{-1}$$ 55 | By associativity property of groups, we know that 56 | $$x^{-1} \cdot (x \cdot \tilde{x}^{-1}) = (x^{-1} \cdot x) \cdot \tilde{x}^{-1}$$ 57 | so, $$x^{-1} \cdot e = e \cdot \tilde{x}^{-1}$$ 58 | which is $$x^{-1} = \tilde{x}^{-1}$$ 59 | So, for any $x \in G$, the inverse $x^{-1}$ is unique. 60 | \end{solution} 61 | 62 | \begin{solution}{2.5}\ 63 | 64 | Let $\alpha = (\begin{smallmatrix}1 & 2 & 3\\ 1 & 3 & 2\end{smallmatrix})$, $\beta = (\begin{smallmatrix}1 & 2 & 3\\ 3 & 1 & 2\end{smallmatrix})$, then, 65 | $$ 66 | \alpha \cdot \beta = 67 | (\begin{smallmatrix}1 & 2 & 3\\ 1 & 3 & 2\end{smallmatrix}) 68 | \cdot (\begin{smallmatrix}1 & 2 & 3\\ 3 & 1 & 2\end{smallmatrix}) 69 | = (\begin{smallmatrix}1 & 2 & 3\\ 3 & 2 & 1\end{smallmatrix}) 70 | $$ 71 | 72 | and 73 | $$ 74 | \beta \cdot \alpha = 75 | (\begin{smallmatrix}1 & 2 & 3\\ 3 & 1 & 2\end{smallmatrix}) \cdot 76 | (\begin{smallmatrix}1 & 2 & 3\\ 1 & 3 & 2\end{smallmatrix}) 77 | = (\begin{smallmatrix}1 & 2 & 3\\ 2 & 1 & 3\end{smallmatrix}) 78 | $$ 79 | 80 | So, we can see that 81 | $$ 82 | (\begin{smallmatrix}1 & 2 & 3\\ 3 & 2 & 1\end{smallmatrix}) 83 | \neq 84 | (\begin{smallmatrix}1 & 2 & 3\\ 2 & 1 & 3\end{smallmatrix}) 85 | $$ 86 | 87 | so, $\alpha \cdot \beta \neq \beta \cdot \alpha$. 88 | \end{solution} 89 | 90 | \begin{solution}{2.26}\ 91 | 92 | We want to prove that $f: G \rightarrow H$ is a \emph{monomorphism} iff $\ker f=\{e\}$.\\ 93 | We know that $f$ is a \emph{monomorphism} (\emph{injective}) iff $\forall a, b \in G$, $f(a) = f(b) \Rightarrow a = b$.\\ 94 | Let $a, b \in G$ such that $f(a)=f(b)$. Then 95 | $$f(a) f(b)^{-1} = f(b) (f(b))^{-1} = e$$ 96 | $$f(a) f(b^{-1}) = e$$ 97 | $$f(ab^{-1}) = e$$ 98 | as $\ker f = \{e\}$, then we see that $ab^{-1}=e$, so $a=b$. Thus $f$ is a \emph{monomorphism}. 99 | \end{solution} 100 | 101 | \end{document} 102 | -------------------------------------------------------------------------------- /fft.sage: -------------------------------------------------------------------------------- 1 | # Primitive Root of Unity 2 | def get_primitive_root_of_unity(F, n): 3 | # using the method described by Thomas Pornin in 4 | # https://crypto.stackexchange.com/a/63616 5 | q = F.order() 6 | for k in range(q): 7 | if k==0: 8 | continue 9 | g = F(k) 10 | # g = F.random_element() 11 | if g==0: 12 | continue 13 | w = g ^ ((q-1)/n) 14 | if w^(n/2) != 1: 15 | return g, w 16 | 17 | # Roots of Unity 18 | def get_nth_roots_of_unity(n, primitive_w): 19 | w = [0]*n 20 | for i in range(n): 21 | w[i] = primitive_w^i 22 | return w 23 | 24 | 25 | # fft (Fast Fourier Transform) returns: 26 | # - nth roots of unity 27 | # - Vandermonde matrix for the nth roots of unity 28 | # - Inverse Vandermonde matrix 29 | def fft(F, n): 30 | g, primitive_w = get_primitive_root_of_unity(F, n) 31 | 32 | w = get_nth_roots_of_unity(n, primitive_w) 33 | 34 | ft = matrix(F, n) 35 | for j in range(n): 36 | row = [] 37 | for k in range(n): 38 | row.append(primitive_w^(j*k)) 39 | ft.set_row(j, row) 40 | ft_inv = ft^-1 41 | return w, ft, ft_inv 42 | 43 | 44 | # Fast polynomial multiplication using FFT 45 | def poly_mul(fa, fb, F, n): 46 | w, ft, ft_inv = fft(F, n) 47 | 48 | # compute evaluation points from polynomials fa & fb at the roots of unity 49 | a_evals = [] 50 | b_evals = [] 51 | for i in range(n): 52 | a_evals.append(fa(w[i])) 53 | b_evals.append(fb(w[i])) 54 | 55 | # multiply elements in a_evals by b_evals 56 | c_evals = map(operator.mul, a_evals, b_evals) 57 | c_evals = vector(c_evals) 58 | 59 | # using FFT, convert the c_evals into fc(x) 60 | fc_coef = c_evals*ft_inv 61 | fc2=P(fc_coef.list()) 62 | return fc2, c_evals 63 | 64 | 65 | # Tests 66 | 67 | ##### 68 | # Roots of Unity test: 69 | q = 17 70 | F = GF(q) 71 | n = 4 72 | g, primitive_w = get_primitive_root_of_unity(F, n) 73 | print("generator:", g) 74 | print("primitive_w:", primitive_w) 75 | 76 | w = get_nth_roots_of_unity(n, primitive_w) 77 | print(f"{n}th roots of unity: {w}") 78 | assert w == [1, 13, 16, 4] 79 | 80 | 81 | ##### 82 | # FFT test: 83 | 84 | def isprime(num): 85 | for n in range(2,int(num^1/2)+1): 86 | if num%n==0: 87 | return False 88 | return True 89 | 90 | # list valid values for q 91 | for i in range(20): 92 | if isprime(8*i+1): 93 | print("q =", 8*i+1) 94 | 95 | q = 41 96 | F = GF(q) 97 | n = 4 98 | # q needs to be a prime, s.t. q-1 is divisible by n 99 | assert (q-1)%n==0 100 | print("q =", q, "n = ", n) 101 | 102 | # ft: Vandermonde matrix for the nth roots of unity 103 | w, ft, ft_inv = fft(F, n) 104 | print("nth roots of unity:", w) 105 | print("Vandermonde matrix:") 106 | print(ft) 107 | 108 | fa_eval = vector([3,4,5,9]) 109 | print("fa_eval:", fa_eval) 110 | 111 | # interpolate f_a(x) 112 | fa_coef = ft_inv * fa_eval 113 | print("fa_coef:", fa_coef) 114 | 115 | P. = PolynomialRing(F) 116 | fa = P(list(fa_coef)) 117 | print("f_a(x):", fa) 118 | 119 | # check that evaluating fa(x) at the roots of unity returns the expected values of fa_eval 120 | for i in range(len(fa_eval)): 121 | assert fa(w[i]) == fa_eval[i] 122 | 123 | # go from coefficient form to evaluation form 124 | fa_eval2 = ft * fa_coef 125 | print("fa_eval'", fa_eval) 126 | assert fa_eval2 == fa_eval 127 | 128 | 129 | # Fast polynomial multiplication using FFT 130 | print("\n---------") 131 | print("---Fast polynomial multiplication using FFT") 132 | 133 | n = 8 134 | # q needs to be a prime, s.t. q-1 is divisible by n 135 | assert (q-1)%n==0 136 | print("q =", q, "n = ", n) 137 | 138 | fa=P([1,2,3,4]) 139 | fb=P([1,2,3,4]) 140 | fc_expected = fa*fb 141 | print("fc expected result:", fc_expected) # expected result 142 | print("fc expected coef", fc_expected.coefficients()) 143 | 144 | fc, c_evals = poly_mul(fa, fb, F, n) 145 | print("c_evals=(a_evals*b_evals)=", c_evals) 146 | print("fc:", fc) 147 | assert fc_expected == fc 148 | -------------------------------------------------------------------------------- /ring-signatures.sage: -------------------------------------------------------------------------------- 1 | from hashlib import sha256 2 | 3 | # Ring Signatures 4 | # bLSAG: Back’s Linkable Spontaneous Anonymous Group signatures 5 | # 6 | # A Rust implementation of this scheme can be found at: 7 | # https://github.com/arnaucube/ring-signatures-rs 8 | 9 | def hashToPoint(a): 10 | # TODO use a proper hash-to-point 11 | h = sha256((str(a)).encode('utf-8')) 12 | r = int(h.hexdigest(), 16) * g 13 | return r 14 | 15 | def hash(R, m, A, B, q): 16 | h = sha256(( 17 | str(R) + str(m) + str(A) + str(B) 18 | ).encode('utf-8')) 19 | r = int(h.hexdigest(), 16) 20 | return int(mod(r, q)) 21 | 22 | def print_ring(a): 23 | print("ring of c's:") 24 | for i in range(len(a)): 25 | print(i, a[i]) 26 | print("") 27 | 28 | class Prover: 29 | def __init__(self, F, g): 30 | self.F = F # Z_p 31 | self.g = g # elliptic curve generator 32 | self.q = self.g.order() # order of group 33 | 34 | def new_key(self): 35 | self.w = int(self.F.random_element()) 36 | self.K = self.g * self.w 37 | return self.K 38 | 39 | def sign(self, m, R): 40 | # determine pi (the position of signer's public key in R 41 | pi = -1 42 | for i in range(len(R)): 43 | if self.K == R[i]: 44 | pi = i 45 | break 46 | assert pi>=0 47 | 48 | a = int(self.F.random_element()) 49 | r = [None] * len(R) 50 | # for i \in {1, 2, ..., n} \ {i=pi} 51 | for i in range(0, len(R)): 52 | if i==pi: 53 | continue 54 | 55 | r[i] = int(mod(int(self.F.random_element()), self.q)) 56 | 57 | c = [None] * len(R) 58 | # c_{pi+1} 59 | pi1 = mod(pi + 1, len(R)) 60 | c[pi1] = hash(R, m, a * self.g, hashToPoint(R[pi]) * a, self.q) 61 | 62 | key_image = self.w * hashToPoint(self.K) 63 | 64 | # do c_{i+1} from i=pi+1 to pi-1: 65 | for j in range(0, len(R)-1): 66 | i = mod(pi1+j, len(R)) 67 | i1 = mod(pi1+j +1, len(R)) 68 | 69 | c[i1] = hash(R, m, r[i] * self.g + c[i] * R[i], r[i] * hashToPoint(R[i]) + c[i] * key_image, self.q) 70 | 71 | # compute r_pi 72 | r[pi] = int(mod(a - c[pi] * self.w, self.q)) 73 | print_ring(c) 74 | 75 | return [c[0], r] 76 | 77 | def verify(g, R, m, key_image, sig): 78 | q = g.order() 79 | c1 = sig[0] 80 | r = sig[1] 81 | assert len(R) == len(r) 82 | 83 | # check that key_image \in G (EC), by l * key_image == 0 84 | assert q * key_image == 0 # in sage 0 EC point is represented as (0:1:0) 85 | 86 | 87 | # for i \in 1,2,...,n 88 | c = [None] * len(R) 89 | c[0] = c1 90 | for j in range(0, len(R)): 91 | i = mod(j, len(R)) 92 | i1 = mod(j+1, len(R)) 93 | c[i1] = hash(R, m, r[i] * g + c[i] * R[i], r[i] * hashToPoint(R[i]) + c[i] * key_image, q) 94 | 95 | print_ring(c) 96 | assert c1 == c[0] 97 | 98 | # Tests 99 | import unittest, operator 100 | 101 | # ethereum elliptic curve 102 | p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F 103 | a = 0 104 | b = 7 105 | F = GF(p) 106 | E = EllipticCurve(F, [a,b]) 107 | GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 108 | GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 109 | g = E(GX,GY) 110 | n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 111 | h = 1 112 | q = g.order() 113 | assert is_prime(p) 114 | assert is_prime(q) 115 | assert g * q == 0 116 | 117 | 118 | class TestRingSignatures(unittest.TestCase): 119 | def test_bLSAG_ring_of_5(self): 120 | test_bLSAG(5, 3) 121 | def test_bLSAG_ring_of_20(self): 122 | test_bLSAG(20, 14) 123 | 124 | def test_bLSAG(ring_size, pi): 125 | print(f"[bLSAG] Testing with a ring of {ring_size} keys") 126 | prover = Prover(F, g) 127 | n = ring_size 128 | R = [None] * n 129 | 130 | # generate prover's key pair 131 | K_pi = prover.new_key() 132 | 133 | # generate other n public keys 134 | for i in range(0, n): 135 | R[i] = g * i 136 | 137 | # set K_pi 138 | R[pi] = K_pi 139 | 140 | # sign m 141 | m = 1234 142 | print("sign") 143 | sig = prover.sign(m, R) 144 | 145 | print("verify") 146 | key_image = prover.w * hashToPoint(prover.K) 147 | verify(g, R, m, key_image, sig) 148 | 149 | if __name__ == '__main__': 150 | unittest.main() 151 | -------------------------------------------------------------------------------- /fft-notes.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{amssymb} 7 | \usepackage{enumerate} 8 | \usepackage{hyperref} 9 | \hypersetup{ 10 | colorlinks, 11 | citecolor=black, 12 | filecolor=black, 13 | linkcolor=black, 14 | urlcolor=blue 15 | } 16 | 17 | \theoremstyle{definition} 18 | \newtheorem{definition}{Def}[section] 19 | \newtheorem{theorem}[definition]{Thm} 20 | \newtheorem{innersolution}{} 21 | \newenvironment{solution}[1] 22 | {\renewcommand\theinnersolution{#1}\innersolution} 23 | {\endinnersolution} 24 | 25 | 26 | \title{FFT: Fast Fourier Transform} 27 | \author{arnaucube} 28 | \date{August 2022} 29 | 30 | \begin{document} 31 | 32 | \maketitle 33 | 34 | \begin{abstract} 35 | Usually while reading papers and books I take handwritten notes, this document contains some of them re-written to $LaTeX$. 36 | 37 | The notes are not complete, don't include all the steps neither all the proofs. I use these notes to revisit the concepts after some time of reading the topic. 38 | 39 | This document are notes done while reading about the topic from \cite{gstrang}, \cite{tpornin}, \cite{rfateman}. 40 | \end{abstract} 41 | 42 | \tableofcontents 43 | 44 | \section{Discrete \& Fast Fourier Transform} 45 | 46 | \subsection{Discrete Fourier Transform (DFT)} 47 | 48 | Continuous: 49 | 50 | $$ 51 | x(f) = \int_{-\infty}^{\infty} x(t) e^{-2 \pi f t} dt 52 | $$ 53 | 54 | Discrete: 55 | The $k^{th}$ frequency, evaluating at $n$ of $N$ samples. 56 | $$ 57 | \hat{f_k} = \sum_{n=0}^{n-1} f_n e^{\frac{-j \pi kn}{N}} 58 | $$ 59 | 60 | where we can group under $b_n = \frac{\pi kn}{N}$. The previous expression can be expanded into: 61 | $$ 62 | x_k = x_0 e^{-b_0 j} + x_1 e^{-b_1 j} + ... + x_n e^{-b_n j} 63 | $$ 64 | 65 | By the \emph{Euler's formula} we have $e^{jx} = cos(x) + j\cdot sin(x)$, and using it in the previous $x_k$, we obtain 66 | 67 | $$ 68 | x_k = x_0 [cos(-b_0) + j \cdot sin(-b_0)] + \ldots 69 | $$ 70 | 71 | Using $\hat{f_k}$ we obtained 72 | $$ 73 | \{f_0, f_1, \ldots, f_N\} \xrightarrow{DFT} \{ \hat{f_0}, \hat{f_1}, \ldots, \hat{f_N} \} 74 | $$ 75 | 76 | To reverse the $\hat{f_k}$ back to $f_k$: 77 | $$ 78 | f_k = \left( \sum_{n=0}^{n-1} \hat{f_n} e^{\frac{-j \pi kn}{N}} \right) \cdot \frac{1}{N} 79 | $$ 80 | 81 | 82 | $$ 83 | DFT = 84 | \begin{pmatrix} 85 | \hat{f_0}\\ 86 | \hat{f_1}\\ 87 | \hat{f_2}\\ 88 | \vdots\\ 89 | \hat{f_n}\\ 90 | \end{pmatrix}= 91 | \begin{pmatrix} 92 | 1 & 1 & 1 & \ldots & 1 \\ 93 | 1 & w_n & w_n^2 & \ldots & w_n^{N-1} \\ 94 | 1 & w_n^2 & w_n^4 & \ldots & w_n^{2(N-1)} \\ 95 | \vdots & \vdots & \vdots & & \vdots\\ 96 | 1 & w_n^{n-1} & w_n^{2(n-1)} & \ldots & w_n^{(N-1)^2} \\ 97 | \end{pmatrix} 98 | \begin{pmatrix} 99 | f_0 \\ f_1 \\ f_2 \\ \vdots \\ f_n 100 | \end{pmatrix} 101 | $$ 102 | 103 | \subsection{Fast Fourier Transform (FFT)} 104 | While DFT is $O(n)$, FFT is $O(n \space log(n))$ 105 | 106 | Here you can find a simple implementation of the these concepts in Rust: \href{https://github.com/arnaucube/fft-rs}{arnaucube/fft-rs} \cite{fftrs} 107 | 108 | 109 | \section{FFT over finite fields, roots of unity, and polynomial multiplication} 110 | 111 | FFT is very useful when working with polynomials. [TODO poly multiplication] 112 | 113 | An implementation of the FFT over finite fields using the Vandermonde matrix approach can be found at \cite{fftsage}. 114 | 115 | \subsection{Intro} 116 | Let $A(x)$ be a polynomial of degree $n-1$, 117 | 118 | $$ 119 | A(x) = a_0 + a_1 \cdot x + a_2 \cdot x^2 + \cdots + a_{n-1} \cdot x^{n-1} = \sum_{i=0}^{n-1} a_i \cdot x^i 120 | $$ 121 | 122 | We can represent $A(x)$ in its evaluation form, 123 | 124 | $$ 125 | (x_0, A(x_0)), (x_1, A(x_1)), \cdots, (x_{n-1}, A(x_{n-1})) = (x_i, A(x_i)) 126 | $$ 127 | 128 | 129 | We can evaluate A(x) at n given points $(x_0, x_1, ..., x_{n-1}$): 130 | 131 | $$ 132 | \begin{pmatrix} 133 | A(x_0)\\ A(x_1)\\ A(x_2)\\ \vdots\\ A(x_{n-1}) 134 | \end{pmatrix}= 135 | \begin{pmatrix} 136 | x_0^0 & x_0^1 & x_0^2 & \ldots & x_0^{n-1} \\ 137 | x_1^0 & x_1^1 & x_1^2 & \ldots & x_1^{n-1} \\ 138 | x_2^0 & x_2^1 & x_2^2 & \ldots & x_2^{n-1} \\ 139 | \vdots & \vdots & \vdots & & \vdots\\ 140 | x_{n-1}^0 & x_{n-1}^1 & x_{n-1}^2 & \ldots & x_{n-1}^{n-1} \\ 141 | \end{pmatrix} 142 | \begin{pmatrix} 143 | a_0 \\ a_1 \\ a_2 \\ \vdots \\ a_{n-1} 144 | \end{pmatrix} 145 | $$ 146 | 147 | This is known by the Vandermonde matrix. 148 | 149 | But this will not be too efficient. Instead of random $x_i$ values, we use \emph{roots of unity}, where $\omega_n^n = 1$. We denote $\omega$ as a primitive $n^{th}$ root of unity: 150 | 151 | $$ 152 | \begin{pmatrix} 153 | A(1)\\ A(\omega)\\ A(\omega^2)\\ \vdots\\ A(\omega^{n-1}) 154 | \end{pmatrix}= 155 | \begin{pmatrix} 156 | 1 & 1 & 1 & \ldots & 1 \\ 157 | 1 & \omega & \omega^2 & \ldots & \omega^{n-1} \\ 158 | 1 & \omega^2 & \omega^4 & \ldots & \omega^{2(n-1)} \\ 159 | \vdots & \vdots & \vdots & & \vdots\\ 160 | 1 & \omega^{n-1} & \omega^{2(n-1)} & \ldots & \omega^{(n-1)^2} \\ 161 | \end{pmatrix} 162 | \begin{pmatrix} 163 | a_0 \\ a_1 \\ a_2 \\ \vdots \\ a_{n-1} 164 | \end{pmatrix} 165 | $$ 166 | 167 | Which we can see as 168 | 169 | $$ 170 | \hat{A} = F_n \cdot A 171 | $$ 172 | 173 | This matches our system of equations: 174 | 175 | \begin{itemize} 176 | \item at $x=0$, $a_0 + a_1 + \cdots + a_{n-1} = A_0 = A(1)$ 177 | \item at $x=1$, $a_0 \cdot 1 + a_1 \cdot \omega + a_2 \cdot \omega^2 + \cdots + a_{n-1} \cdot \omega^{n-1} = A_1 = A(\omega)$ 178 | \item at $x=2$, $a_0 \cdot 1 + a_1 \cdot \omega^2 + a_2 \cdot \omega^4 + \cdots + a_{n-1} \cdot \omega^{2(n-1)} = A_2 = A(\omega^2)$ 179 | \item $\cdots$ 180 | \item at $x=n-1$, $a_0 \cdot 1 + a_1 \cdot \omega^{n-1} + a_2 \cdot \omega^{2(n-1)} + \cdots + a_{n-1} \cdot \omega^{(n-1)(n-1)} = A_2 = A(\omega^{n-1})$ 181 | \end{itemize} 182 | 183 | We denote the $F_n$ as the Fourier matrix, with $j$ rows and $k$ columns, where each entry can be expressed as $F_{jk} = \omega^{jk}$. 184 | 185 | To find the $a_i$ values, we use the inverted $F_n = F_n^{-1}$ 186 | 187 | \subsection{Roots of unity} 188 | todo 189 | 190 | \subsection{FFT over finite fields} 191 | todo 192 | 193 | \subsection{Polynomial multiplication with FFT} 194 | todo 195 | 196 | 197 | \bibliography{fft-notes.bib} 198 | \bibliographystyle{unsrt} 199 | 200 | \end{document} 201 | -------------------------------------------------------------------------------- /notes_sonic.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{enumerate} 7 | \usepackage{hyperref} 8 | \hypersetup{ 9 | colorlinks, 10 | citecolor=black, 11 | filecolor=black, 12 | linkcolor=black, 13 | urlcolor=blue 14 | } 15 | \usepackage{xcolor} 16 | 17 | % prevent warnings of underfull \hbox: 18 | \usepackage{etoolbox} 19 | \apptocmd{\sloppy}{\hbadness 4000\relax}{}{} 20 | 21 | \theoremstyle{definition} 22 | \newtheorem{definition}{Def}[section] 23 | \newtheorem{theorem}[definition]{Thm} 24 | 25 | 26 | \title{Notes on Sonic} 27 | \author{arnaucube} 28 | \date{April 2022} 29 | 30 | \begin{document} 31 | 32 | \maketitle 33 | 34 | \begin{abstract} 35 | Notes taken while reading Sonic paper \cite{cryptoeprint:2019/099}. Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 36 | 37 | The notes are not complete, don't include all the steps neither all the proofs. 38 | \end{abstract} 39 | 40 | \tableofcontents 41 | 42 | 43 | \section{Sonic} 44 | 45 | \subsection{Structured Reference String} 46 | $\{ \{g^{x^i}\}_{i=-d}^d, \{ g^{\alpha x^i} \}_{i=-d, i \neq 0}^d, \{ h^{x^i}, h^{\alpha x^i} \}_{i=-d}^d, e(g, h^\alpha) \}$ 47 | 48 | \subsection{System of constraints} 49 | Multiplication constraint: $a \cdot b = c$ 50 | 51 | $Q$ linear constraints: 52 | $$ 53 | a \cdot u_q + b \cdot v_q + c \cdot w_q = k_q 54 | $$ 55 | 56 | with $u_q, v_q, w_q \in \mathbb{F}^n$, and $k_q \in \mathbb{F}_p$. 57 | 58 | \vspace{0.5cm} 59 | Example: $x^2 + y^2 = z$ 60 | 61 | $$a = (x, y), \qquad b = (x, y), \qquad c = (x^2, y^2)$$ 62 | \begin{enumerate}[i.] 63 | \item $(x, y) \cdot (1, 0) + (x, y) \cdot (-1, 0) + (x^2, y^2) \cdot (0, 0) = 0 \longrightarrow x - x = 0$ 64 | \item $(x, y) \cdot (0, 1) + (x, y) \cdot (0, -1) + (x^2, y^2) \cdot (0, 0) = 0 \longrightarrow y - y = 0$ 65 | \item $(x, y) \cdot (0, 0) + (x, y) \cdot (0, 0) + (x^2, y^2) \cdot (1, 1) = z \longrightarrow x^2 + y^2 = z$ 66 | \end{enumerate} 67 | 68 | So, 69 | $$u_1 = (1, 0) \quad v_1=(-1, 0) \quad w_1=(0, 0) \quad k_1=0$$ 70 | $$u_2 = (0, 1) \quad v_2=(0, -1) \quad w_2=(0, 0) \quad k_2=0$$ 71 | $$u_3 = (0, 0) \quad v_3=(0, 0) \quad w_3=(1, 1) \quad k_2=z$$ 72 | 73 | \vspace{1cm} 74 | 75 | Compress n multiplication constraints into an equation in formal indeterminate $Y$: 76 | $$\sum_{i=1}^n (a_i b_i - c_i) \cdot Y^i = 0$$ 77 | encode into negative exponents of $Y$: 78 | $$\sum_{i=1}^n (a_i b_i - c_i) \cdot Y^-i = 0$$ 79 | 80 | Also, compress the $Q$ linear constraints, scaling by $Y^n$ to preserve linear independence: 81 | $$ 82 | \sum_{q=1}^Q (a \cdot u_q + b \cdot v_q + c \cdot w_q - k_q) \cdot Y^{q+n} = 0 83 | $$ 84 | 85 | Polys: 86 | 87 | \begin{align} 88 | \nonumber & u_i(Y) = \sum_{q=1}^Q Y^{q+n} \cdot u_{q, i}\\ 89 | \nonumber & v_i(Y) = \sum_{q=1}^Q Y^{q+n} \cdot v_{q, i}\\ 90 | \nonumber & w_i(Y) = -Y^i - Y^{-1} + \sum_{q=1}^Q Y^{q+n} \cdot w_{q, i}\\ 91 | \nonumber & k(Y) = \sum_{q=1}^Q Y^{q+n} \cdot k_q 92 | \end{align} 93 | 94 | Combine the multiplicative and linear constraints to: 95 | 96 | \begin{align} 97 | \nonumber & a \cdot u(Y) + b \cdot v(Y) + c \cdot w(Y) 98 | + \sum_{i=1}^n a_i b_i (Y^i + Y^{-i}) - k(Y) = 0 99 | \end{align} 100 | 101 | where $a \cdot u(Y) + b \cdot v(Y) + c \cdot w(Y)$ is embedded into the constant term of the polynomial $t(X, Y)$. 102 | 103 | 104 | Define $r(X, Y)$ s.t. $r(X, Y) = r(XY, 1)$. 105 | 106 | $$\Longrightarrow r(X, Y) = \sum_{i=1}^n (a_i X^i Y^i + b_i X^{-i} Y^{-i} + c_i X^{-i-n} Y^{-i-n})$$ 107 | 108 | $$s(X, Y) = \sum_{i=1}^n (u_i(Y) X^{-i} + v_i(Y) X^i + w_i(Y) X^{i+n})$$ 109 | 110 | $$r'(X, Y) = r(X, Y) + s(X, Y)$$ 111 | $$t(X, Y) = r(X, Y) + r'(X, Y) - k(Y)$$ 112 | 113 | The coefficient of $X^0$ in $t(X, Y)$ is the left-hand side of the equation. 114 | 115 | Sonic demonstrates that the constant term of $t(X, Y)$ is zero, thus demonstrating that our constraint system is satisfied. 116 | 117 | 118 | \subsubsection{The basic Sonic protocol} 119 | 120 | \begin{enumerate}[1.] 121 | \item Prover constructs $r(X, Y)$ using their hidden witness 122 | \item Prover commits to $r(X, 1)$, setting the maximum degree to n 123 | \item Verifier sends random challenge $y$ 124 | \item Prover commits to $t(X, y)$. The commitment scheme ensures that $t(X, y)$ has no constant term. 125 | \item Verifier sends random challenge $z$ 126 | \item Prover opens commitments to $r(z, 1), r(z, y), t(z, y)$ 127 | \item Verifier calculates $r'(z, y)$, and checks that 128 | $$r(z, y) \cdot r'(z, y) - k(y) == t(z, y)$$ 129 | \end{enumerate} 130 | 131 | Steps $3$ and $5$ can be made non-interactive by the Fiat-Shamir transformation. 132 | 133 | \subsubsection{Polynomial Commitment Scheme} 134 | Sonic uses an adaptation of KZG \cite{kzg-tmp}, want: 135 | 136 | \begin{enumerate}[i.] 137 | \item \emph{evaluation binding}, i.e. given a commitment $F$, an adversary cannot open F to two different evaluations $v_1$ and $v_2$ 138 | \item \emph{bounded polynomial extractable}, i.e. any algebraic adversary that opens a commitment $F$ knows an opening $f(X)$ with powers $-d \leq i \leq max, i \neq 0$. 139 | \end{enumerate} 140 | 141 | \vspace{0.5cm} 142 | PC scheme (adaptation of KZG): 143 | 144 | \begin{enumerate}[i.] 145 | \item Commit(info, $f(X)$) $\longrightarrow F$: 146 | $$F = g^{\alpha \cdot x^{d-max}} \cdot f(x)$$ 147 | \item Open(info, $F$, $z$, $f(x)$) $\longrightarrow (f(z), W)$: 148 | $$w(X) = \frac{f(X) - f(z)}{X-z}$$ 149 | $$W = g^{w(x)}$$ 150 | \item Verify(info, $F$, $z$, $(v, W)$) $\longrightarrow 0/1$:\\ 151 | Check: 152 | $$e(W, h^{\alpha \cdot x}) \cdot 153 | e(g^v W^{-z}, h^{\alpha}) 154 | == e(F, h^{x^{-d+max}})$$ 155 | \end{enumerate} 156 | 157 | \subsection{Succinct signatures of correct computation} 158 | Signature of correct computation to ensure that an element $s=s(z, y)$ for a known polynomial 159 | $$s(X, Y) = \sum_{i, j = -d}^d s_{i, j} \cdot X^i \cdot Y^i$$ 160 | 161 | Use the structure of $s(X, Y)$ to prove its correct calculation using a \emph{permutation argument} $\longrightarrow$ \emph{grand-product argument} inspired by Bayer and Groth, and Bootle et al. 162 | 163 | Restrict to constraint systems where $s(X, Y)$ can be expressed as the sum of $M$ polynomials. Where $j-th$ poly is of the form: 164 | $$ 165 | \Psi_j(X, Y) = 166 | \sum_{i=1}^n \psi_{j, \sigma_{j, i}} 167 | \cdot X^i \cdot Y^{\sigma_{j, i}} 168 | $$ 169 | 170 | where $\sigma_j$ is the fixed polynomial permutation, and $\phi_{j, i} \in \mathbb{F}$ are the coefficients. 171 | 172 | \vspace{1cm} 173 | \framebox{WIP} 174 | \vspace{1cm} 175 | 176 | 177 | 178 | \bibliography{paper-notes.bib} 179 | \bibliographystyle{unsrt} 180 | 181 | \end{document} 182 | -------------------------------------------------------------------------------- /paper-notes.bib: -------------------------------------------------------------------------------- 1 | @misc{cryptoeprint:2021/529, 2 | author = {Nicolas Gailly and Mary Maller and Anca Nitulescu}, 3 | title = {SnarkPack: Practical SNARK Aggregation}, 4 | howpublished = {Cryptology ePrint Archive, Paper 2021/529}, 5 | year = {2021}, 6 | note = {\url{https://eprint.iacr.org/2021/529}}, 7 | url = {https://eprint.iacr.org/2021/529} 8 | } 9 | 10 | @misc{cryptoeprint:2019/099, 11 | author = {Mary Maller and Sean Bowe and Markulf Kohlweiss and Sarah Meiklejohn}, 12 | title = {Sonic: Zero-Knowledge SNARKs from Linear-Size Universal and Updateable Structured Reference Strings}, 13 | howpublished = {Cryptology ePrint Archive, Paper 2019/099}, 14 | year = {2019}, 15 | note = {\url{https://eprint.iacr.org/2019/099}}, 16 | url = {https://eprint.iacr.org/2019/099} 17 | } 18 | 19 | @misc{kzg-tmp, 20 | author = {A. Kate and G. M. Zaverucha and and I. Goldberg}, 21 | title = {Constant-size commitments to polynomials and their application}, 22 | year = {2010}, 23 | note = {\url{https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf}}, 24 | url = {https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf} 25 | } 26 | 27 | @misc{bls-sig-eth2, 28 | author = {Eth2.0}, 29 | title = {Eth2.0 book - BLS signatures}, 30 | year = {2010}, 31 | note = {\url{https://eth2book.info/altair/part2/building_blocks/signatures}}, 32 | url = {https://eth2book.info/altair/part2/building_blocks/signatures} 33 | } 34 | @misc{cryptoeprint:2019/1021, 35 | author = {Sean Bowe and Jack Grigg and Daira Hopwood}, 36 | title = {Recursive Proof Composition without a Trusted Setup}, 37 | howpublished = {Cryptology ePrint Archive, Paper 2019/1021}, 38 | year = {2019}, 39 | note = {\url{https://eprint.iacr.org/2019/1021}}, 40 | url = {https://eprint.iacr.org/2019/1021} 41 | } 42 | 43 | @misc{cryptoeprint:2022/621, 44 | author = {Arantxa Zapico and Vitalik Buterin and Dmitry Khovratovich and Mary Maller and Anca Nitulescu and Mark Simkin}, 45 | title = {Caulk: Lookup Arguments in Sublinear Time}, 46 | howpublished = {Cryptology ePrint Archive, Paper 2022/621}, 47 | year = {2022}, 48 | note = {\url{https://eprint.iacr.org/2022/621}}, 49 | url = {https://eprint.iacr.org/2022/621} 50 | } 51 | 52 | @misc{cryptoeprint:2022/957, 53 | author = {Jim Posen and Assimakis A. Kattis}, 54 | title = {Caulk+: Table-independent lookup arguments}, 55 | howpublished = {Cryptology ePrint Archive, Paper 2022/957}, 56 | year = {2022}, 57 | note = {\url{https://eprint.iacr.org/2022/957}}, 58 | url = {https://eprint.iacr.org/2022/957} 59 | } 60 | 61 | @misc{fri, 62 | author = {Eli Ben-Sasson and Iddo Bentov and Yinon Horesh and Michael Riabzev}, 63 | title = {Fast Reed-Solomon Interactive Oracle Proofs of Proximity}, 64 | year = {2018}, 65 | note = {\url{https://eccc.weizmann.ac.il/report/2017/134/}}, 66 | url = {https://eccc.weizmann.ac.il/report/2017/134/} 67 | } 68 | @misc{cryptoeprint:2022/1216, 69 | author = {Ulrich Haböck}, 70 | title = {A summary on the FRI low degree test}, 71 | howpublished = {Cryptology ePrint Archive, Paper 2022/1216}, 72 | year = {2022}, 73 | note = {\url{https://eprint.iacr.org/2022/1216}}, 74 | url = {https://eprint.iacr.org/2022/1216} 75 | } 76 | @misc{cryptoeprint:2019/1020, 77 | author = {Alexander Vlasov and Konstantin Panarin}, 78 | title = {Transparent Polynomial Commitment Scheme with Polylogarithmic Communication Complexity}, 79 | howpublished = {Cryptology ePrint Archive, Paper 2019/1020}, 80 | year = {2019}, 81 | note = {\url{https://eprint.iacr.org/2019/1020}}, 82 | url = {https://eprint.iacr.org/2019/1020} 83 | } 84 | @misc{cryptoeprint:2024/390, 85 | author = {Gal Arnon and Alessandro Chiesa and Giacomo Fenzi and Eylon Yogev}, 86 | title = {{STIR}: Reed–Solomon Proximity Testing with Fewer Queries}, 87 | howpublished = {Cryptology {ePrint} Archive, Paper 2024/390}, 88 | year = {2024}, 89 | note = {\url{https://eprint.iacr.org/2024/390}}, 90 | url = {https://eprint.iacr.org/2024/390} 91 | } 92 | 93 | 94 | @misc{cryptoeprint:2021/370, 95 | author = {Abhiram Kothapalli and Srinath Setty and Ioanna Tzialla}, 96 | title = {Nova: Recursive Zero-Knowledge Arguments from Folding Schemes}, 97 | howpublished = {Cryptology ePrint Archive, Paper 2021/370}, 98 | year = {2021}, 99 | note = {\url{https://eprint.iacr.org/2021/370}}, 100 | url = {https://eprint.iacr.org/2021/370} 101 | } 102 | 103 | @misc{vincenzoiovino, 104 | title = {{Vincenzo Iovino}}, 105 | note = {\url{https://sites.google.com/site/vincenzoiovinoit/}}, 106 | url = {https://sites.google.com/site/vincenzoiovinoit/} 107 | } 108 | @misc{hectormasipardevol, 109 | title = {{Héctor Masip Ardevol}}, 110 | note = {\url{https://hecmas.github.io}}, 111 | url = {https://hecmas.github.io} 112 | } 113 | @misc{fri-impl, 114 | note = {\url{https://github.com/arnaucube/fri-commitment}}, 115 | url = {https://github.com/arnaucube/fri-commitment} 116 | } 117 | 118 | @misc{cryptoeprint:2019/550, 119 | author = {Srinath Setty}, 120 | title = {Spartan: Efficient and general-purpose zkSNARKs without trusted setup}, 121 | howpublished = {Cryptology ePrint Archive, Paper 2019/550}, 122 | year = {2019}, 123 | note = {\url{https://eprint.iacr.org/2019/550}}, 124 | url = {https://eprint.iacr.org/2019/550} 125 | } 126 | 127 | @misc{cryptoeprint:2023/552, 128 | author = {Srinath Setty and Justin Thaler and Riad Wahby}, 129 | title = {Customizable constraint systems for succinct arguments}, 130 | howpublished = {Cryptology ePrint Archive, Paper 2023/552}, 131 | year = {2023}, 132 | note = {\url{https://eprint.iacr.org/2023/552}}, 133 | url = {https://eprint.iacr.org/2023/552} 134 | } 135 | @misc{cryptoeprint:2023/573, 136 | author = {Abhiram Kothapalli and Srinath Setty}, 137 | title = {HyperNova: Recursive arguments for customizable constraint systems}, 138 | howpublished = {Cryptology ePrint Archive, Paper 2023/573}, 139 | year = {2023}, 140 | note = {\url{https://eprint.iacr.org/2023/573}}, 141 | url = {https://eprint.iacr.org/2023/573} 142 | } 143 | 144 | @ARTICLE{10177902, 145 | author={Satriawan, Ardianto and Syafalni, Infall and Mareta, Rella and Anshori, Isa and Shalannanda, Wervyan and Barra, Aleams}, 146 | journal={IEEE Access}, 147 | title={Conceptual Review on Number Theoretic Transform and Comprehensive Review on Its Implementations}, 148 | year={2023}, 149 | volume={11}, 150 | number={}, 151 | pages={70288-70316}, 152 | keywords={Convolution;Complexity theory;Discrete Fourier transforms;Cryptography;Quantum computing;Homomorphic encryption;Toy manufacturing industry;Quantum computing;Number theoretic transform;post quantum cryptography;homomorphic encryption}, 153 | doi={10.1109/ACCESS.2023.3294446} 154 | } 155 | -------------------------------------------------------------------------------- /sigma-or-notes.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} % theorems structure 5 | \usepackage{enumerate} 6 | \usepackage{hyperref} % links 7 | \usepackage{tikz} % diagrams 8 | \usepackage{pgf-umlsd} % diagrams 9 | \usepackage{amsmath} % arrows 10 | 11 | \theoremstyle{definition} 12 | \newtheorem{example}{Example}[section] 13 | 14 | \title{Sigma protocol and OR proofs - notes} 15 | \author{arnaucube} 16 | \date{March 2022} 17 | 18 | \begin{document} 19 | 20 | \maketitle 21 | 22 | \begin{abstract} 23 | This document contains the notes taken during the \emph{Cryptography Seminars} given by \href{https://github.com/rbkhmrcr}{Rebekah Mercer}. 24 | \end{abstract} 25 | 26 | \tableofcontents 27 | 28 | \section{Sigma protocol} 29 | 30 | \subsection{The protocol} 31 | Let $q$ be a prime, $q$ a prime divisor in $p-1$, and $g$ and element of order $q$ in $\mathbb{Z}_p^a$. Then we have $G = \langle g \rangle$. 32 | \\ 33 | We assume that computationally for a given $A$ it's hard to find $a \in \mathbb{F}$ such that $A = g^a$. 34 | \\ 35 | Alice wants to prove that knows the \emph{witness} $w \in \mathbb{F}$, such that the \emph{statement} $X = g^w$, without revealing $w$. 36 | 37 | \begin{enumerate}[1.] 38 | \item Alice generates a random $a \xleftarrow{r} \mathbb{F}$, and computes $A=g^a$. And sends $A$ to Bob. 39 | \item Bob generates a challenge $c \xleftarrow{r} \mathbb{F}$, and sends it to Alice. 40 | \item Alice computes $z=a + c \cdot w$, and sends it to Bob. 41 | \item Bob verifies it by checking that $g^z == X^c \cdot A$. 42 | \end{enumerate} 43 | 44 | We can unfold Bob's verification and see that: 45 | $$g^z == X^c \cdot A$$ 46 | $$g^{a+cw} == g^{wc} g^a$$ 47 | $$g^{a+cw} == g^{wc+a}$$ 48 | 49 | \begin{center} 50 | \begin{sequencediagram} 51 | \newinst[1]{a}{Alice} 52 | \newinst[3]{b}{Bob} 53 | \mess[0]{a}{$A$}{b} 54 | \mess[2]{b}{$c$}{a} 55 | \mess[2]{a}{$z$}{b} 56 | \mess[0]{b}{$ok$}{a} 57 | \end{sequencediagram} 58 | \end{center} 59 | 60 | Properties: 61 | \begin{enumerate}[i.] 62 | \item \emph{correctness/completness}: if Alice know the witness for the statement, then they can create a valid proof. 63 | \item \emph{soundness}: if someone does not have knowledge of the witness, can not form a valid proof (verifier will always reject). 64 | \item \emph{zero knowledge}: nobody gains knowledge of anything new with the proof. prior knowledge + proof = prior knowledge 65 | \end{enumerate} 66 | 67 | \subsection{Non interactive protocol} 68 | With the \emph{Fiat-Shamir Heuristic}, we model a hash function as a random oracle, thus we can replace Bob's role by a hash function in order to obtain the challenge $c \in \mathbb{F}$. 69 | \\ 70 | So, we replace the step 2 from the described protocol by $c = H(X || A)$ (where $H$ is a known hash function). 71 | 72 | \subsection{What could go wrong (Simulator)} 73 | If the verifier (Bob) sends $c \in \mathbb{F}$, prior to the prover committed to $A$, the prover could create a proof about a public key which they don't know $w$. 74 | \begin{enumerate}[1.] 75 | \item Bob sends $c \xleftarrow{r} \mathbb{F}$ to Alice 76 | \item Alice generates $z \xleftarrow{r} \mathbb{F}$ 77 | \item Alice then computes $A = g^z X^{-c}$, and sends $z, A$ to Bob 78 | \item Bob would check that $g^z == X^c A$ and it would pass the verification, as $g^z== X^c \cdot A \Rightarrow g^z==X^c \cdot g^z X^{-c} \Rightarrow g^z == g^z$. 79 | \end{enumerate} 80 | 81 | As we've seen, it's really important the order of the steps, so Alice must commit to $A$ before knowing $c$.\\ 82 | This 'fake' proof generation is often called the \emph{simulator} and used for further constructions. 83 | 84 | \section{OR proof} 85 | 86 | \emph{OR proofs} allows the prover to prove that they know the witness $w$ of one of the two known \emph{public keys} $X_0, X_1 \in \mathbb{F}$, without revealing which one. It uses the construction seen in the \emph{sigma protocols} together with the idea of the \emph{simulator}. 87 | 88 | A similar construction is used for $n$ statements in the \emph{ring signatures} scheme (used for example in \emph{Monero}). In our case, we will work with $n=2$. 89 | 90 | \subsection{The protocol} 91 | \subsubsection{Simulator} 92 | We can assume that the simulator is a box that for given the inputs $(g, X)$, it will output $(A_s, c_s, z_s)$, such that verification succeeds ($g^{z_s}==X^{c_s} \cdot A_s$). 93 | 94 | \begin{center} 95 | \begin{tikzpicture} 96 | \node [draw, 97 | minimum width=2cm, 98 | minimum height=1.2cm, 99 | right=1cm 100 | ] (simulator) {simulator}; 101 | \draw[-stealth] ++(-1,0) -- (simulator.west) 102 | node[midway,above]{$g, X$}; 103 | \draw[-stealth] (simulator.east) -- ++(+2,0) 104 | node[midway,above]{$A_s, c_s, z_s$}; 105 | \end{tikzpicture} 106 | \end{center} 107 | 108 | Internally, the simulator computes 109 | $$z_s \xleftarrow{r} \mathbb{F},~c_s \xleftarrow{r} \mathbb{F},~A_s = g^{z_s} \cdot X^{c_s}$$ 110 | 111 | \subsection{Flow} 112 | For two known \emph{public keys} $X_0, X_1 \in G$, Alice knows $w_b \in \mathbb{F}$, for $b \in \{0, 1\}$, such that $g^{w_b} = X_0$ or $g^{w_b} = X_1$. As we don't know if Alice controls $0$ or $1$, from now on, we will use $b$ and $1-b$. 113 | \\ 114 | So, Alice knows $w_b \in \mathbb{F}$ such that $X_b = g^{w_b}$, and does not know $w_{1-b}$ for $X_{1-b}=g^{w_{1-b}}$. 115 | 116 | \begin{enumerate} 117 | \item First of all, as in the \emph{Sigma protocol}, Alice generates a random \emph{commitment} $a_b \xleftarrow{r} \mathbb{F}$, and computes $A_b = g^{a_b}$. 118 | \item Then, Alice will run the \emph{simulator} for $1-b$. 119 | \begin{list}{} 120 | \item Sets a random $c_{1-b} \xleftarrow{r} \mathbb{F}$, and runs the simulator with inputs\\$(c_{1-b}, X_{1-b})$, and outputs $(A_{1-b}, c_{1-b}, z_{1-b})$. 121 | \item Remember that internally the \emph{simulator} will set random\\ 122 | $z_{1-b}, c_{1-b} \xleftarrow{r} \mathbb{F}$, and compute an $A_{1-b}$ such that\\ 123 | $A_{1-b} = g^{z_{1-b}} \cdot X_{1-b}^{c_{1-b}}$. 124 | \end{list} 125 | \item Now, Alice sends $A_b, A_{1-b}$ to Bob 126 | \item And Bob sends back the \emph{challenge} $s \xleftarrow{r} \mathbb{F}$. 127 | \item Alice then splits the challenge $s$ into $c_b, c_{1-b}$, by $s = c_{1-b} \oplus c_b$. So Alice can compute $c_b = s \oplus c_{1-b}$. 128 | \item Then Alice computes $z_b = a_b \cdot w_b + c_b$. And sends to Bob $(c_b, c_{1-b}, z_b, z_{1-b})$. 129 | \item Bob can perform the verification by checking that: 130 | \begin{enumerate}[i.] 131 | \item $s == c_b \oplus c_{1-b}$ 132 | \item $g_{z_{1-b}} == A_{1-b} \cdot X_{1-b}^{-c_{1-b}}$ 133 | \item $g_{z_b} == A_b \cdot X_b^{-c_b}$ 134 | \end{enumerate} 135 | \end{enumerate} 136 | 137 | 138 | 139 | \begin{center} 140 | \begin{sequencediagram} 141 | \newinst[1]{a}{Alice} 142 | \newinst[3]{b}{Bob} 143 | \mess[1]{a}{$A_b, A_{1-b}$}{b} 144 | \mess[1]{b}{$s$}{a} 145 | \mess[1]{a}{$c_b, c_{1-b}, z_b, z_{1-b}$}{b} 146 | \end{sequencediagram} 147 | \end{center} 148 | 149 | \section{Resources} 150 | \begin{enumerate} 151 | \item \href{https://cs.au.dk/~ivan/Sigma.pdf}{https://cs.au.dk/~ivan/Sigma.pdf} 152 | \item \emph{Cryptography Made Simple}, Nigel Smart. Section 21.3. 153 | \end{enumerate} 154 | 155 | \end{document} 156 | -------------------------------------------------------------------------------- /notes_halo.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{enumerate} 7 | \usepackage{hyperref} 8 | \hypersetup{ 9 | colorlinks, 10 | citecolor=black, 11 | filecolor=black, 12 | linkcolor=black, 13 | urlcolor=blue 14 | } 15 | \usepackage{xcolor} 16 | 17 | \usepackage{pgf-umlsd} % diagrams 18 | % message between threads 19 | % Example: 20 | % \bloodymess[delay]{sender}{message content}{receiver}{DIR}{start note}{end note} 21 | \newcommand{\bloodymess}[7][0]{ 22 | \stepcounter{seqlevel} 23 | \path 24 | (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess from) {}; 25 | \addtocounter{seqlevel}{#1} 26 | \path 27 | (#4)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess to) {}; 28 | \draw[->,>=angle 60] (mess from) -- (mess to) node[midway, above] 29 | {#3}; 30 | 31 | \if R#5 32 | \node (#3 from) at (mess from) {\llap{#6~}}; 33 | \node (#3 to) at (mess to) {\rlap{~#7}}; 34 | \else\if L#5 35 | \node (#3 from) at (mess from) {\rlap{~#6}}; 36 | \node (#3 to) at (mess to) {\llap{#7~}}; 37 | \else 38 | \node (#3 from) at (mess from) {#6}; 39 | \node (#3 to) at (mess to) {#7}; 40 | \fi 41 | \fi 42 | } 43 | 44 | % prevent warnings of underfull \hbox: 45 | \usepackage{etoolbox} 46 | \apptocmd{\sloppy}{\hbadness 4000\relax}{}{} 47 | 48 | \theoremstyle{definition} 49 | \newtheorem{definition}{Def}[section] 50 | \newtheorem{theorem}[definition]{Thm} 51 | 52 | 53 | \title{Notes on Halo} 54 | \author{arnaucube} 55 | \date{July 2022} 56 | 57 | \begin{document} 58 | 59 | \maketitle 60 | 61 | \begin{abstract} 62 | Notes taken while reading Halo paper \cite{cryptoeprint:2019/1021}. Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 63 | 64 | The notes are not complete, don't include all the steps neither all the proofs. 65 | \end{abstract} 66 | 67 | \tableofcontents 68 | 69 | \section{modified IPA (from Halo paper)} 70 | Notes taken while reading about the modified Inner Product Argument (IPA) from the Halo paper \cite{cryptoeprint:2019/1021}. 71 | 72 | \paragraph{Objective:} 73 | Prover wants to prove that the polynomial $p(X)$ from the commitment $P$ evaluates to $v$ at $x$, and that $deg(p(X)) \leq d-1$. 74 | 75 | \subsection{Notation} 76 | \begin{description} 77 | \item[Scalar mul] $[a]G$, where $a$ is a scalar and $G \in \mathbb{G}$ 78 | \item[Inner product] $<\overrightarrow{a}, \overrightarrow{b}> = a_0 b_0 + a_1 b_1 + \ldots + a_{n-1} b_{n-1}$ 79 | \item[Multiscalar mul] $<\overrightarrow{a}, \overrightarrow{G}> = [a_0] G_0 + [a_1] G_1 + \ldots + [a_{n-1}] G_{n-1}$ 80 | \end{description} 81 | 82 | 83 | \subsection{Transparent setup} 84 | $\overrightarrow{G} \in^r \mathbb{G}^d$, $H \in^r \mathbb{G}$ 85 | 86 | Prover wants to commit to $p(x)=a_0$ 87 | \subsection{Protocol} 88 | Prover: 89 | $$P=<\overrightarrow{a}, \overrightarrow{G}> + [r]H$$ 90 | $$v=<\overrightarrow{a}, \{1, x, x^2, \ldots, x^{d-1} \} >$$ 91 | 92 | where $\{1, x, x^2, \ldots, x^{d-1} \} = \overrightarrow{b}$. 93 | 94 | We can see that computing $v$ is the equivalent to evaluating $p(X)$ at $x$ ($p(x)=v$). 95 | 96 | We will prove: 97 | \begin{enumerate}[i.] 98 | \item polynomial $p(X) = \sum a_i X^i$\\ 99 | $p(x) = v$ (that $p(X)$ evaluates $x$ to $v$). 100 | \item $deg(p(X)) \leq d-1$ 101 | \end{enumerate} 102 | 103 | 104 | Both parties know $P$, point $x$ and claimed evaluation $v$. For $U \in^r \mathbb{G}$. 105 | 106 | Prover computes $P'$: 107 | 108 | $$P' = P + [v] U = <\overrightarrow{a}, G> + [r]H + [v] U$$ 109 | 110 | Now, for $k$ rounds ($d=2^k$, from $j=k$ to $j=1$): 111 | \begin{itemize} 112 | \item Prover sets random blinding factors: $l_j, r_j \in \mathbb{F}_p$ 113 | \item Prover computes 114 | $$L_j = < \overrightarrow{a}_{lo}, \overrightarrow{G}_{hi}> + [l_j] H + [< \overrightarrow{a}_{lo}, \overrightarrow{b}_{hi}>] U$$ 115 | $$R_j = < \overrightarrow{a}_{hi}, \overrightarrow{G}_{lo}> + [r_j] H + [< \overrightarrow{a}_{hi}, \overrightarrow{b}_{lo}>] U$$ 116 | \item Verifier sends random challenge $u_j \in \mathbb{I}$ 117 | \item Prover computes the halved vectors for next round: 118 | $$\overrightarrow{a} \leftarrow \overrightarrow{a}_{hi} \cdot u_j^{-1} + \overrightarrow{a}_{lo} \cdot u_j$$ 119 | $$\overrightarrow{b} \leftarrow \overrightarrow{b}_{lo} \cdot u_j^{-1} + \overrightarrow{b}_{hi} \cdot u_j$$ 120 | $$\overrightarrow{G} \leftarrow \overrightarrow{G}_{lo} \cdot u_j^{-1} + \overrightarrow{G}_{hi} \cdot u_j$$ 121 | \end{itemize} 122 | 123 | After final round, $\overrightarrow{a}, \overrightarrow{b}, \overrightarrow{G}$ are each of length 1. 124 | 125 | Verifier can compute 126 | $$G = \overrightarrow{G}_0 = < \overrightarrow{s}, \overrightarrow{G} >$$ 127 | and $$b = \overrightarrow{b}_0 = < \overrightarrow{s}, \overrightarrow{b} >$$ 128 | where $\overrightarrow{s}$ is the binary counting structure: 129 | 130 | \begin{align*} 131 | &s = (u_1^{-1} ~ u_2^{-1} \cdots ~u_k^{-1},\\ 132 | &~~~~~~u_1 ~~~ u_2^{-1} ~\cdots ~u_k^{-1},\\ 133 | &~~~~~~u_1^{-1} ~~ u_2 ~~\cdots ~u_k^{-1},\\ 134 | &~~~~~~~~~~~~~~\vdots\\ 135 | &~~~~~~u_1 ~~~~ u_2 ~~\cdots ~u_k) 136 | \end{align*} 137 | 138 | 139 | And verifier checks: 140 | $$[a]G + [r'] H + [ab] U == P' + \sum_{j=1}^k ( [u_j^2] L_j + [u_j^{-2}] R_j)$$ 141 | 142 | where the synthetic blinding factor $r'$ is $r' = r + \sum_{j=1}^k (l_j u_j^2 + r_j u_j^{-2})$. 143 | 144 | \vspace{1cm} 145 | 146 | Unfold: 147 | 148 | $$ 149 | \textcolor{brown}{[a]G} + \textcolor{cyan}{[r'] H} + \textcolor{magenta}{[ab] U} 150 | == 151 | \textcolor{blue}{P'} + \sum_{j=1}^k ( \textcolor{violet}{[u_j^2] L_j} + \textcolor{orange}{[u_j^{-2}] R_j}) 152 | $$ 153 | 154 | \begin{align*} 155 | &Left~side = \textcolor{brown}{[a]G} + \textcolor{cyan}{[r'] H} + \textcolor{magenta}{[ab] U}\\ 156 | & = \textcolor{brown}{< \overrightarrow{a}, \overrightarrow{G} >}\\ 157 | &+ \textcolor{cyan}{[r + \sum_{j=1}^k (l_j \cdot u_j^2 + r_j u_j^{-2})] \cdot H}\\ 158 | &+ \textcolor{magenta}{< \overrightarrow{a}, \overrightarrow{b} > U} 159 | \end{align*} 160 | 161 | 162 | \begin{align*} 163 | &Right~side = \textcolor{blue}{P'} + \sum_{j=1}^k ( \textcolor{violet}{[u_j^2] L_j} + \textcolor{orange}{[u_j^{-2}] R_j})\\ 164 | &= \textcolor{blue}{< \overrightarrow{a}, \overrightarrow{G}> + [r] H + [v] U}\\ 165 | &+ \sum_{j=1}^k ( 166 | \textcolor{violet}{[u_j^2] \cdot <\overrightarrow{a}_{lo}, \overrightarrow{G}_{hi}> + [l_j] H + [<\overrightarrow{a}_{lo}, \overrightarrow{b}_{hi}>] U}\\ 167 | &\textcolor{orange}{+ [u_j^{-2}] \cdot <\overrightarrow{a}_{hi}, \overrightarrow{G}_{lo}> + [r_j] H + [<\overrightarrow{a}_{hi}, \overrightarrow{b}_{lo}>] U} 168 | ) 169 | \end{align*} 170 | 171 | 172 | \vspace{1.5cm} 173 | The following diagram ilustrates the main steps in the scheme: 174 | 175 | \begin{center} 176 | \begin{sequencediagram} 177 | \newinst[1]{p}{Prover} 178 | \newinst[3]{v}{Verifier} 179 | 180 | \bloodymess[1]{p}{P}{v}{R}{knows $p(X)\in \mathbb{F[X]}$, commits to $p(X)$, $P$}{rand $x \in \mathbb{F},~U\in \mathbb{G},~\overrightarrow{u} \in \mathbb{F}^d$} 181 | \bloodymess[1]{v}{$x, U, u$}{p}{R}{}{} 182 | \bloodymess[1]{p}{$proof, a, L_j, R_j, v$}{v}{R}{gen proof}{$verify(proof, P, a, x, L_j, R_j)$} 183 | 184 | % \begin{callself}{p}{knows $p(X) \in \mathbb{F}[X]$}{} 185 | % \end{callself} 186 | % \begin{callself}{p}{commit to $p(X),~P$}{} 187 | % \end{callself} 188 | % 189 | % \mess[0]{p}{$P$}{v} 190 | % \begin{callself}{v}{rand $x \in \mathbb{F},~U\in \mathbb{G},~\overrightarrow{u} \in \mathbb{F}^d$}{} 191 | % \end{callself} 192 | % 193 | % \mess[0]{v}{$x,U,u$}{p} 194 | 195 | % \node[anchor=west] (p2) at (mess to) {gen proof2} 196 | 197 | % \begin{callself}{p}{gen proof $\pi$}{} 198 | % \end{callself} 199 | % 200 | % \mess[0]{p}{$a, L_j, R_j, v$}{v} 201 | % 202 | % \begin{callself}{v}{$verify(P, a, x, v, L_j, R_k$)}{} 203 | % \end{callself} 204 | \end{sequencediagram} 205 | \end{center} 206 | 207 | \section{Amortization Strategy} 208 | TODO 209 | 210 | \bibliography{paper-notes.bib} 211 | \bibliographystyle{unsrt} 212 | 213 | \end{document} 214 | -------------------------------------------------------------------------------- /sigma.sage: -------------------------------------------------------------------------------- 1 | from hashlib import sha256 2 | 3 | # Implementation of Sigma protocol & OR proofs 4 | 5 | 6 | def hash_two_points(a, b): 7 | h = sha256((str(a)+str(b)).encode('utf-8')) 8 | return int(h.hexdigest(), 16) 9 | 10 | def generic_verify(g, X, A, c, z): 11 | return g * int(z) == X * int(c) + A 12 | 13 | ### 14 | # Sigma protocol interactive 15 | ### 16 | 17 | class Prover_interactive: 18 | def __init__(self, F, g): 19 | self.F = F # Z_q 20 | self.g = g # elliptic curve generator 21 | 22 | def new_key(self): 23 | self.w = self.F.random_element() 24 | X = self.g * int(self.w) 25 | return X 26 | 27 | def new_commitment(self): 28 | self.a = self.F.random_element() 29 | A = self.g * int(self.a) 30 | return A 31 | 32 | def gen_proof(self, c): 33 | return int(self.a) + int(c) * int(self.w) 34 | 35 | class Verifier_interactive: 36 | def __init__(self, F, g): 37 | self.F = F 38 | self.g = g 39 | 40 | def new_challenge(self, A): 41 | self.A = A 42 | self.c = self.F.random_element() 43 | return self.c 44 | 45 | def verify(self, X, z): 46 | return self.g * int(z) == X * int(self.c) + self.A 47 | 48 | 49 | ### 50 | # Sigma protocol non-interactive 51 | ### 52 | class Prover: 53 | def __init__(self, F, g): 54 | self.F = F # Z_p 55 | self.g = g # elliptic curve generator 56 | 57 | def new_key(self): 58 | self.w = self.F.random_element() 59 | X = self.g * int(self.w) 60 | return X 61 | 62 | def gen_proof(self, X): 63 | a = self.F.random_element() 64 | A = self.g * int(a) 65 | c = hash_two_points(A, X) 66 | z = int(a) + c * int(self.w) 67 | return A, z 68 | 69 | 70 | class Verifier: 71 | def __init__(self, F, g): 72 | self.F = F 73 | self.g = g 74 | 75 | def verify(self, X, A, z): 76 | c = hash_two_points(A, X) 77 | return self.g * int(z) == X * c + A 78 | 79 | class Simulator: 80 | def __init__(self, F, g): 81 | self.F = F 82 | self.g = g 83 | 84 | def simulate(self, X): 85 | c = self.F.random_element() 86 | z = self.F.random_element() 87 | # A = g * int(z) + X*(-int(c)) 88 | A = g * int(z) - X * int(c) 89 | return A, c, z 90 | 91 | ### 92 | # OR proof (with 2 parties) 93 | ### 94 | 95 | class ORProver_2parties: 96 | def __init__(self, F, g): 97 | self.F = F # Z_p 98 | self.g = g # elliptic curve generator 99 | 100 | def new_key(self): 101 | self.w = self.F.random_element() 102 | X = self.g * int(self.w) 103 | return X 104 | 105 | def gen_commitments(self, xs): 106 | # gen commitment A 107 | self.a = self.F.random_element() 108 | A = self.g * int(self.a) 109 | 110 | # run the simulator for 1-b 111 | sim = Simulator(self.F, self.g) 112 | A_1, c_1, z_1 = sim.simulate(xs[1]) 113 | 114 | self.A_1 = A_1 115 | self.c_1 = c_1 116 | self.z_1 = z_1 117 | 118 | return [A, A_1] 119 | 120 | def gen_proof(self, s): 121 | # split the challenge s = c xor c_1 122 | c = int(s) ^^ int(self.c_1) 123 | # compute z 124 | z = int(self.a) + int(c) * int(self.w) 125 | # note, here the order of the returned elements is always the same, in 126 | # a real-world implementation would be shuffled 127 | return [c, self.c_1], [z, self.z_1] 128 | 129 | class ORVerifier_2parties: 130 | def __init__(self, F, g): 131 | self.F = F 132 | self.g = g 133 | 134 | def new_challenge(self, As): 135 | self.As = As 136 | self.s = self.F.random_element() 137 | return self.s 138 | 139 | def verify(self, Xs, cs, zs): 140 | assert self.s == int(cs[0]) ^^ int(cs[1]) 141 | assert self.g * int(zs[0]) == Xs[0] * int(cs[0]) + self.As[0] 142 | assert self.g * int(zs[1]) == Xs[1] * int(cs[1]) + self.As[1] 143 | 144 | ### 145 | # OR proof (with n parties) 146 | ### 147 | 148 | class ORProver: 149 | def __init__(self, F, g): 150 | self.F = F # Z_p 151 | self.g = g # elliptic curve generator 152 | 153 | def new_key(self): 154 | self.w = self.F.random_element() 155 | X = self.g * int(self.w) 156 | return X 157 | 158 | def gen_commitments(self, xs): 159 | # gen commitment A 160 | self.a = self.F.random_element() 161 | A = self.g * int(self.a) 162 | self.As = [A] 163 | 164 | 165 | # run the simulator for the rest of Xs 166 | sim = Simulator(self.F, self.g) 167 | self.cs = [] 168 | self.zs = [] 169 | for i in range(1, len(xs)): 170 | A_1, c_1, z_1 = sim.simulate(xs[i]) 171 | self.As.append(A_1) 172 | self.cs.append(c_1) 173 | self.zs.append(z_1) 174 | 175 | return self.As 176 | 177 | def gen_proof(self, s): 178 | # split the challenge s = c xor c_1 xor c_2 xor ... xor c_n 179 | c = int(s) 180 | for i in range(len(self.cs)): 181 | c = c ^^ int(self.cs[i]) 182 | 183 | self.cs.insert(0, c) # add c at the beginning of cs array 184 | 185 | # compute z 186 | z = int(self.a) + int(c) * int(self.w) 187 | self.zs.insert(0, z) # add z at the beginning of zs array 188 | 189 | # note, here the order of the returned elements is always the same, in 190 | # a real-world implementation would be shuffled 191 | return self.cs, self.zs 192 | 193 | class ORVerifier: 194 | def __init__(self, F, g): 195 | self.F = F 196 | self.g = g 197 | 198 | def new_challenge(self, As): 199 | self.As = As 200 | self.s = self.F.random_element() 201 | return self.s 202 | 203 | def verify(self, Xs, cs, zs): 204 | # check s == c_0 xor c_1 xor c_2 xor ... xor c_n 205 | computed_s = int(cs[0]) 206 | for i in range(1, len(cs)): 207 | computed_s = computed_s ^^ int(cs[i]) 208 | 209 | assert self.s == computed_s 210 | 211 | # check g*z == X*c + A (in multiplicative notation would g^z ==X^c * A) 212 | for i in range(len(Xs)): 213 | assert self.g * int(zs[i]) == Xs[i] * int(cs[i]) + self.As[i] 214 | 215 | 216 | 217 | # Tests 218 | import unittest, operator 219 | 220 | # ethereum elliptic curve 221 | p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F 222 | a = 0 223 | b = 7 224 | F = GF(p) 225 | E = EllipticCurve(F, [a,b]) 226 | GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 227 | GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 228 | g = E(GX,GY) 229 | n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 230 | h = 1 231 | q = g.order() 232 | assert is_prime(p) 233 | assert is_prime(q) 234 | 235 | class TestSigmaProtocol(unittest.TestCase): 236 | def test_interactive(self): 237 | alice = Prover_interactive(F, g) 238 | 239 | # Alice generates witness w & statement X 240 | X = alice.new_key() 241 | assert X == alice.g * int(alice.w) 242 | 243 | # Alice generates the commitment A 244 | A = alice.new_commitment() 245 | assert A == alice.g * int(alice.a) 246 | 247 | # Bob generates the challenge (and stores A) 248 | bob = Verifier_interactive(F, g) 249 | c = bob.new_challenge(A) 250 | 251 | # Alice generates the proof 252 | z = alice.gen_proof(c) 253 | 254 | # Bob verifies the proof 255 | assert bob.verify(X, z) 256 | 257 | # check with the generic_verify function 258 | assert generic_verify(g, X, A, c, z) 259 | 260 | def test_non_interactive(self): 261 | alice = Prover(F, g) 262 | 263 | # Alice generates witness w & statement X 264 | X = alice.new_key() 265 | assert X == alice.g * int(alice.w) 266 | 267 | # Alice generates the proof 268 | A, z = alice.gen_proof(X) 269 | 270 | # Bob generates the challenge 271 | bob = Verifier(F, g) 272 | 273 | # Bob verifies the proof 274 | assert bob.verify(X, A, z) 275 | 276 | # check with the generic_verify function 277 | c = hash_two_points(A, X) 278 | assert generic_verify(g, X, A, c, z) 279 | 280 | def test_simulator(self): 281 | sim = Simulator(F, g) 282 | 283 | # set a public key X, for which we don't know w 284 | unknown_w = 3 285 | X = g * unknown_w 286 | 287 | # simulate for X 288 | A, c, z = sim.simulate(X) 289 | 290 | # verify the simulated proof 291 | assert generic_verify(g, X, A, c, z) 292 | 293 | class TestORProof(unittest.TestCase): 294 | def test_2_parties(self): 295 | # set a public key X, for which we don't know w 296 | unknown_w = 3 297 | X_1 = g * unknown_w 298 | 299 | alice = ORProver_2parties(F, g) 300 | 301 | # Alice generates key pair 302 | X = alice.new_key() 303 | Xs = [X, X_1] 304 | # Alice generates commitments (internally running the simulator) 305 | As = alice.gen_commitments(Xs) 306 | 307 | # Bob generates the challenge (and stores As) 308 | bob = ORVerifier_2parties(F, g) 309 | s = bob.new_challenge(As) 310 | 311 | # Alice generates the ORproof 312 | cs, zs = alice.gen_proof(s) 313 | 314 | # Bob verifies the proofs 315 | bob.verify(Xs, cs, zs) 316 | 317 | def test_n_parties(self): 318 | # set n public keys X, for which we don't know w 319 | Xs = [] 320 | for i in range(10): 321 | X_i = g * i 322 | Xs.append(X_i) 323 | 324 | alice = ORProver(F, g) 325 | 326 | # Alice generates key pair 327 | X = alice.new_key() 328 | Xs.insert(0, X) # add X at the beginning of Xs array 329 | 330 | # Alice generates commitments (internally running the simulator) 331 | As = alice.gen_commitments(Xs) 332 | 333 | # Bob generates the challenge (and stores As) 334 | bob = ORVerifier(F, g) 335 | s = bob.new_challenge(As) 336 | 337 | # Alice generates the ORproof 338 | cs, zs = alice.gen_proof(s) 339 | 340 | # Bob verifies the proofs 341 | bob.verify(Xs, cs, zs) 342 | 343 | if __name__ == '__main__': 344 | unittest.main() 345 | -------------------------------------------------------------------------------- /notes_ntt.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{enumerate} 7 | 8 | \usepackage{hyperref} 9 | \hypersetup{ 10 | colorlinks, 11 | citecolor=black, 12 | filecolor=black, 13 | linkcolor=black, 14 | urlcolor=blue 15 | } 16 | 17 | 18 | \newcommand{\Zq}{\mathbb{Z}_q} 19 | \newcommand{\Rq}{\mathbb{Z}_q[X]/(X^n+1)} 20 | 21 | 22 | \title{NTT for Negacyclic Polynomial Multiplication} 23 | \author{arnaucube} 24 | \date{January 2025} 25 | 26 | \begin{document} 27 | 28 | \maketitle 29 | 30 | \begin{abstract} 31 | Notes taken while studying the NTT, mostly from \cite{10177902}. 32 | 33 | Usually while reading books and papers I take handwritten notes in a notebook, this document contains some of them re-written to $LaTeX$. 34 | 35 | The notes are not complete, don't include all the steps neither all the proofs. 36 | 37 | Update: an implementation of the NTT can be found at\\ 38 | \href{https://github.com/arnaucube/fhe-study/blob/main/arith/src/ntt.rs}{https://github.com/arnaucube/fhe-study/blob/main/arith/src/ntt.rs}. 39 | \end{abstract} 40 | 41 | \tableofcontents 42 | 43 | \section{Main idea} 44 | For doing multiplications in the \emph{negacyclic polynomial ring} ($\Rq$), rather than doing it in a naive way, it is more 45 | efficient to do it through the NTT. 46 | 47 | This is, let $a(X), b(X) \in \Rq$, and suppose we want to 48 | obtain $a(X) \cdot b(X)$. First apply the NTT to the two ring 49 | elements that we want to multiply, 50 | $$\hat{a}(X) = NTT(a(X)),~~ \hat{b}(X)=NTT(b(X))$$ 51 | then multiply the result element-wise, 52 | % $$\hat{c}(X) = \sum \hat{a}_i \cdot \hat{b}_i$$ 53 | $$c= \hat{a} \circ \hat{b}$$ 54 | where $\circ$ means the element-wise vector multiplication in $\Zq$. 55 | 56 | Then apply the NTT$^{-1}$ to the result, obtaining the actual value of 57 | multiplying $a(X) \cdot b(X)$. 58 | 59 | \section{Cyclotomic vs Negacyclic} 60 | 61 | \subsection{Cyclotomic: \texorpdfstring{$\mathbb{Z}_q[X]/(X^n-1)$}{Zq[X]/(X**n-1)}} 62 | In the cyclotomic case, the primitive n-th root of unity in $Z_q$ is $w^n \equiv 1 \pmod q$ (and 63 | $w^k \not\equiv 1 \pmod q ~~ for k deg(g):~ r ~\text{is not finite at}~ 0$ 95 | \item if $deg(f) = deg(g)$ with $deg(f)$ even:\\ 96 | $f$'s canonical form leading terms $ax^d$\\ 97 | $g$'s canonical form leading terms $bx^d$\\ 98 | $a,b \in \Bbbk^\times,~ d=\frac{deg(f)}{2}$, set $r(0)=\frac{a}{b}$ 99 | \item if $deg(f) = deg(g)$ with $deg(f)$ odd\\ 100 | $f$'s canonical form leading terms $ax^d$\\ 101 | $g$'s canonical form leading terms $bx^d$\\ 102 | $a,b \in \Bbbk^\times,~ deg(f)=deg(g)=3+2d$, set $r(0)=\frac{a}{b}$ 103 | \end{enumerate} 104 | \end{definition} 105 | 106 | \subsection{Zeros, poles, uniformizers and multiplicities} 107 | 108 | $r \in \Bbbk(E)$ has a \emph{zero} in $P\in E$ if $r(P)=0$\\ 109 | $r \in \Bbbk(E)$ has a \emph{pole} in $P\in E$ if $r(P)$ is not finite. 110 | 111 | \paragraph{uniformizer:} Let $P\in E$, 112 | uniformizer: rational function $u \in \Bbbk(E)$ with $u(P)=0$ if 113 | $\forall r\in \Bbbk(E) \setminus \{0\},~ \exists d \in \mathbb{Z},~ s\in \Bbbk(E)$ finite at $P$ with $s(P) \neq 0$ s.t. 114 | $$r=u^d \cdot s$$ 115 | 116 | \paragraph{order:} Let $P \in E(\Bbbk)$, let $u \in \Bbbk(E)$ be a uniformizer at $P$. 117 | For $r \in \Bbbk(E) \setminus \{0\}$ being a rational function with $r=u^d \cdot s$ with $s(P)\neq 0, \infty$, we say that $r$ has \emph{order} $d$ at $P$ ($ord_P(r)=d$). 118 | 119 | \paragraph{multiplicity:} \emph{multiplicity of a zero} of $r$ is the order of $r$ at that point, \emph{multiplicity of a pole} of $r$ is the order of $r$ at that point. 120 | 121 | if $P \in E(\Bbbk)$ is neither a zero or pole of $r$, then $ord_P(r)=0$ ($=d,~ r=u^0s$). 122 | 123 | \vspace{0.5cm} 124 | \begin{minipage}{4.3 in} 125 | \paragraph{Multiplicities, from the book "Elliptic Tales"} (p.69), to provide intuition 126 | 127 | Factorization into \emph{linear factors}: $p(x)=c\cdot (x-a_1) \cdots (x-a_d)$\\ 128 | $d$: degree of $p(x)$, $a_i \in \Bbbk$\\ 129 | Solutions to $p(x)=0$ are $x=a_1, \ldots, a_d$ (some $a_i$ can be repeated)\\ 130 | eg.: $p(x)=(x-1)(x-1)(x-3)$, solutions to $p(x)=0:~ 1, 1, 3$\\ 131 | $x=1$ is a solution to $p(x)=0$ of \emph{multiplicity} 2. 132 | 133 | The total number of solutions (counted with multiplicity) is $d$, the degree of the polynomial whose roots we are finding. 134 | \end{minipage} 135 | 136 | 137 | \section{Divisors} 138 | 139 | \begin{definition}{Divisor} 140 | $$D= \sum_{P \in E(\Bbbk)} n_p \cdot [P]$$ 141 | \end{definition} 142 | 143 | \begin{definition}{Degree \& Sum} 144 | $$deg(D)= \sum_{P \in E(\Bbbk)} n_p$$ 145 | $$sum(D)= \sum_{P \in E(\Bbbk)} n_p \cdot P$$ 146 | \end{definition} 147 | 148 | 149 | The set of all divisors on $E$ forms a group: for $D = \sum_{P\in E(\Bbbk)} n_P[P]$ and $D' = \sum_{P\in E(\Bbbk)} m_P[P]$, 150 | $$D+D' = \sum_{P\in E(\Bbbk)} (n_P + m_P)[P]$$ 151 | 152 | \begin{definition}{Associated divisor} 153 | $$div(r) = \sum_{P \in E(\Bbbk)} ord_P(r)[P]$$ 154 | \end{definition} 155 | 156 | Observe that 157 | \begin{enumerate} 158 | \item[] $div(rs) = div(r)+div(s)$ 159 | \item[] $div(\frac{r}{s}) = div(r)-div(s)$ 160 | \end{enumerate} 161 | 162 | Observe that 163 | $$\sum_{P \in E(\Bbbk)} ord_P(r) \cdot P = 0$$ 164 | 165 | \begin{definition}{Support of a divisor} 166 | $$\sum_P n_P[P], ~\forall P \in E(\Bbbk) ~\text{s.t.}~ n_P \neq 0$$ 167 | \end{definition} 168 | 169 | \begin{definition}{Principal divisor} 170 | iff 171 | $$deg(D)=0$$ 172 | $$sum(D)=0$$ 173 | \end{definition} 174 | $D \sim D'$ iff $D - D'$ is principal. 175 | 176 | 177 | \begin{definition}{Evaluation of a rational function} (function $r$ evaluated at $D$) 178 | $$r(D)= \prod r(P)^{n_p}$$ 179 | \end{definition} 180 | 181 | \section{Weil reciprocity} 182 | \begin{theorem}{(Weil reciprocity)} 183 | Let $E/ \Bbbk$ be an e.c. over an algebraically closed field. If $r,~s \in \Bbbk\setminus \{0\}$ are rational functions whose divisors have disjoint support, then 184 | $$r(div(s)) = s(div(r))$$ 185 | \end{theorem} 186 | Proof. (todo) 187 | 188 | \paragraph{Example} 189 | \begin{align*} 190 | p(x)=x^2 - 1,&~ q(x)=\frac{x}{x-2}\\ 191 | div(p)&= 1 \cdot [1] + 1 \cdot [-1] - 2 \cdot [\infty]\\ 192 | div(q)&= 1 \cdot [0] - 1 \cdot [2]\\ 193 | &\text{(they have disjoint support)}\\ 194 | p(div(q)) &= p(0)^1 \cdot p(2)^{-1}= (0^2 - 1)^1 \cdot (2^2 - 1)^{-1} = \frac{-1}{3}\\ 195 | q(div(p)) &= q(1)^1 \cdot q(-1)^1 - q(\infty)^2\\ 196 | &= (\frac{1}{1-2})^1 \cdot (\frac{-1}{-1-2})^1 \cdot (\frac{\infty}{\infty - 2})^2 = \frac{-1}{3} 197 | \end{align*} 198 | 199 | so, $p(div(q))=q(div(p))$. 200 | 201 | \section{Generic Weil Pairing} 202 | Let $E(\Bbbk)$, with $\Bbbk$ of char $p$, $n$ s.t. $p \nmid n$. 203 | 204 | $\Bbbk$ large enough: $E(\Bbbk)[n] = E(\overline{\Bbbk}) = \mathbb{Z}_n \oplus \mathbb{Z}_n$ (with $n^2$ elements). 205 | 206 | For $P, Q \in E[n]$, 207 | \begin{align*} 208 | D_P &\sim [P] - [0]\\ 209 | D_Q &\sim [Q] - [0] 210 | \end{align*} 211 | 212 | We need them to have disjoint support: 213 | \begin{align*} 214 | D_P &\sim [P] - [0]\\ 215 | D_Q' &\sim [Q+T] - [T] 216 | \end{align*} 217 | 218 | $$\Delta D = D_Q - D_Q' = [Q] - [0] - [Q+T] + [T]$$ 219 | 220 | 221 | Note that $n D_P$ and $n D_Q$ are principal. Proof: 222 | \begin{align*} 223 | n D_P &= n [P] - n [O]\\ 224 | deg(n D_P) &= n - n = 0\\ 225 | sum(n D_P) &= nP - nO = 0 226 | \end{align*} 227 | ($nP = 0$ bcs. $P$ is n-torsion) 228 | 229 | Since $n D_P,~ n D_Q$ are principal, we know that $f_P,~ f_Q$ exist. 230 | 231 | Take 232 | \begin{align*} 233 | f_P &: div(f_P) = n D_P\\ 234 | f_Q &: div(f_Q) = n D_Q 235 | \end{align*} 236 | 237 | We define 238 | $$ 239 | e_n(P, Q) = \frac{f_P(D_Q)}{f_Q(D_P)} 240 | $$ 241 | 242 | Remind: evaluation of a rational function over a divisor $D$: 243 | \begin{align*} 244 | D &= \sum n_P [P]\\ 245 | r(D) &= \prod r(P)^{n_P} 246 | \end{align*} 247 | 248 | If $D_P = [P+S] - [S],~~ D_Q=[Q-T]-[T]$ what is $e_n(P, Q)$? 249 | 250 | \begin{align*} 251 | f_P(D_Q) &= f_P(Q+T)^1 \cdot f_P(T)^{-1}\\ 252 | f_Q(D_P) &= f_Q(P+S)^1 \cdot f_Q(S)^{-1} 253 | \end{align*} 254 | 255 | $$ 256 | e_n(P, Q) = \frac{f_P(Q+T)}{f_P(T)} / \frac{f_Q(P+S)}{f_Q(S)} 257 | $$ 258 | 259 | with $S \neq \{O, P, -Q, P-Q \}$. 260 | 261 | 262 | \section{Properties} 263 | \begin{enumerate}[i.] 264 | \item $e_n(P, Q)^n = 1 ~\forall P,Q \in E[n]$\\ 265 | ($\Rightarrow~ e_n(P,Q)$ is a $n^{th}$ root of unity) 266 | \item Bilinearity 267 | $$e_n(P_1+P_2, Q) = e_n(P_1, Q) \cdot e_n(P_2, Q)$$ 268 | $$e_n(P, Q_1+Q_2) = e_n(P, Q_1) \cdot e_n(P, Q_2)$$ 269 | \emph{proof:} 270 | recall that $e_n(P,Q)=\frac{g(S+P)}{g(S)}$, then, 271 | \begin{align*} 272 | e_n(P_1, Q) &\cdot e_n(P_2, Q) = \frac{g(P_1 + S)}{g(S)} \cdot \frac{g(P_2 + P_1 + S)}{g(P_1 + S)}\\ 273 | &\text{(replace $S$ by $S+P_1$)}\\ 274 | &= \frac{g(P_2 + P_1 + S)}{g(S)} = e_n(P_1+P_2, Q) 275 | \end{align*} 276 | \item Alternating 277 | $$e_n(P, P)=1 ~\forall P\in E[n]$$ 278 | \item Nondegenerate 279 | $$\text{if}~ e_n(P,Q)=1 ~\forall Q\in E[n],~ \text{then}~ P=0$$ 280 | \end{enumerate} 281 | 282 | 283 | \section{Exercises} 284 | \emph{An Introduction to Mathematical Cryptography, 2nd Edition} - Section 6.8. Bilinear pairings on elliptic curves 285 | 286 | \begin{solution}{6.29} 287 | $div(R(x) \cdot S(x)) = div( R(x)) + div( S(x))$, where $R(x), S(x)$ are rational functions. 288 | \\proof:\\ 289 | \emph{Norm} of $f$: $N_f = f \cdot \overline{f}$, and we know that $N_{fg} = N_f \cdot N_g~\forall~\Bbbk[E]$,\\ 290 | then $$deg(f) = deg_x(N_f)$$\\ 291 | and $$deg(f \cdot g) = deg(f) + deg(g)$$ 292 | 293 | Proof: 294 | $$deg(f \cdot g) = deg_x(N_{fg}) = deg_x(N_f \cdot N_g)$$ 295 | $$= deg_x(N_f) + deg_x(N_g) = deg(f) + deg(g)$$ 296 | 297 | So, $\forall P \in E(\Bbbk),~ ord_P(rs) = ord_P(r) + ord_P(s)$.\\ 298 | As $div(r) = \sum_{P\in E(\Bbbk)} ord_P(r)[P]$, $div(s) = \sum ord_P(s)[P]$. 299 | 300 | So, 301 | $$div(rs) = \sum ord_P(rs)[P]$$ 302 | $$= \sum ord_P(r)[P] + \sum ord_P(s)[P] = div(r) + div(s)$$ 303 | \end{solution} 304 | 305 | \vspace{0.5cm} 306 | 307 | \begin{solution}{6.31} 308 | $$e_m(P, Q) = e_m(Q, P)^{-1} \forall P, Q \in E[m]$$ 309 | Proof: 310 | We know that $e_m(P, P) = 1$, so: 311 | $$1 = e_m(P+Q, P+Q) = e_m(P, P) \cdot e_m(P, Q) \cdot e_m(Q, P) \cdot e_m(Q, Q)$$ 312 | 313 | and we know that $e_m(P, P) = 1$, then we have: 314 | $$1 = e_m(P, Q) \cdot e_m(Q, P)$$ 315 | $$\Longrightarrow e_m(P, Q) = e_m(Q, P)^{-1}$$ 316 | \end{solution} 317 | 318 | 319 | 320 | 321 | \end{document} 322 | -------------------------------------------------------------------------------- /notes_reed-solomon.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | \setlength{\parindent}{0em} 3 | \setlength{\parskip}{0.4em} 4 | \usepackage{amsmath} 5 | \usepackage{amsfonts} 6 | \usepackage{amsthm} 7 | \usepackage{enumerate} 8 | \usepackage[bookmarksopen=true,bookmarks=true]{hyperref} 9 | \usepackage{bookmark} 10 | \hypersetup{ 11 | pdfborder = {0.1 0.1 0.1}, 12 | % colorlinks, 13 | % citecolor=black, 14 | % filecolor=black, 15 | % linkcolor=black, 16 | % urlcolor=blue 17 | } 18 | 19 | \usepackage{multicol} 20 | \usepackage{graphicx} 21 | \usepackage{float} 22 | \graphicspath{ {./imgs} } 23 | \usepackage{pgf-umlsd} % diagrams 24 | \usepackage[UKenglish]{datetime} 25 | \newdateformat{DATEMonthYear}{\monthname[\THEMONTH] \THEYEAR} 26 | \newdateformat{DATEYearMonthDay}{\THEYEAR-\THEMONTH-\THEDAY} 27 | 28 | \usepackage{booktabs} % \toprule \midrule ... 29 | 30 | \theoremstyle{definition} 31 | \newtheorem{definition}{Def}[section] 32 | \newtheorem{theorem}[definition]{Thm} 33 | 34 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 | 36 | 37 | \title{Notes on Reed-Solomon codes} 38 | \author{arnaucube} 39 | \date{January 2023} 40 | 41 | \begin{document} 42 | 43 | \maketitle 44 | 45 | \begin{abstract} 46 | Notes taken while reading about Reed-Solomon codes. Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 47 | 48 | The notes are not complete, don't include all the steps neither all the proofs. 49 | \end{abstract} 50 | 51 | \tableofcontents 52 | 53 | \section{Reed-Solomon Codes overview} 54 | In this section we overview the main ideas presented in the Reed-Solomon paper \cite{reedsolomon} and in Bruce Maggs notes \cite{reedsolomon-bruce}. 55 | 56 | Reed-Solomon codes appeared in parallel to the BCH codes, and can be described as nonbinary BCH codes. In this section we will focus only in the Reed-Solomon codes, particularly in the encoding and error detection (not correction). 57 | 58 | \emph{tmp-note}: I feel like it is worth to check Galois theory \& BCH codes before going to Reed-Solomon codes, but due time constraints and our main goal being FRI, probably we should skip it. 59 | 60 | \subsection{Idea} 61 | 62 | Let $p(x) = m_0 + m_1 x + \ldots + m_{k-1} x^{k-1}$, where $m_i \in K$ and $k < 2^n$. 63 | 64 | We map $k$-tuples of $K$ into $2^n$-tuples of $K$, where $K=GF(p^r)$. 65 | % Code E maps $k$-tuples of $K$ into $2^n$-tuples of $K$, where $K$ is a field of degree $n$ over the field of two elements $\mathbb{Z}_2$. 66 | $$ 67 | k 68 | \begin{cases} 69 | m_0\\ 70 | m_1\\ 71 | \vdots\\ 72 | m_{k-1} 73 | \end{cases} 74 | \in K~~~~ 75 | % \stackrel{E}{ \xrightarrow{\hspace*{1cm}} } 76 | \xrightarrow{\hspace*{1cm}} 77 | 2^n 78 | \begin{cases} 79 | p(0)\\ 80 | p(\alpha)\\ 81 | p(\alpha^2)\\ 82 | \vdots\\ 83 | p(1) 84 | \end{cases} 85 | \in K 86 | $$ 87 | 88 | The receiver then, can decode the messages by solving simultaneously any $k$ of the $2^n$ equations 89 | 90 | \begin{align*} 91 | p(0) &= m_0\\ 92 | p(\alpha) &= m_0 + m_1 \alpha + m_2 \alpha^2 + \ldots + m_{k-1} \alpha^{k-1}\\ 93 | p(\alpha^2) &= m_0 + m_1 \alpha^2 + m_2 \alpha^4 + \ldots + m_{k-1} \alpha^{2k-2}\\ 94 | &~~~\vdots\\ 95 | p(1) &= m_0 + m_1 + m_2 + \ldots + m_{k-1}\\ 96 | \end{align*} 97 | 98 | This system of equations can be solved, as we can see that any $k$ of the $p(\alpha^j)$ equations are linearly independent since they form a Vandermonde matrix 99 | $$ 100 | V = 101 | \begin{pmatrix} 102 | 1 & x_1 & x_1^2 & \ldots & x_1^{k-1} \\ 103 | 1 & x_2 & x_2^2 & \ldots & x_2^{k-1} \\ 104 | 1 & x_3 & x_3^2 & \ldots & x_3^{k-1} \\ 105 | \vdots & \vdots & \vdots & & \vdots\\ 106 | 1 & x_n & x_n^2 & \ldots & x_n^{k-1} \\ 107 | \end{pmatrix} 108 | $$ 109 | 110 | and $\det V$ is a Vandermonde determinant and $\det V \neq 0$ as 111 | $$ 112 | \det~V = \prod_{j < i} (x_i - x_j) 113 | $$ 114 | 115 | See Annex \ref{sec:vandermondedet} for a proof of the Vandermonde determinant. 116 | 117 | % The number of combinations of taking $k$ elements from $2^n$ elements (without regard of order) can be expressed by $C_k^{2^n} = \binom{2^n}{k} = \frac{(2^n)!}{k!~(2^n - k)!}$. 118 | % This is the number of determinations of $(m_0, \ldots, m_{m-1})$ in case of no errors. 119 | 120 | % TODO this part needs to be continued & finished. 121 | 122 | \subsection{Real world approach} 123 | In the practical side, instead of transmitting $k+2s$ polynomial evaluations, we send $k$ coefficients + $2s$ evaluations. 124 | 125 | This is because we are interested in efficiency in the \emph{common} case, where in most of cases there are no errors and we care more about having a fast check that there are no errors than of the error recovering phase. 126 | 127 | Furthermore, in our use case in the context of FRI IOP, we are not interested into decoding but only into detecting errors. 128 | 129 | % \subsubsection{The setup} 130 | % We work on $GF(p^r)$. For $\alpha$ being a primitive element of $GF(p^r)$, we set the generator polynomial 131 | % 132 | % $$ 133 | % g(x) = (x-\alpha) (x-\alpha^2) \cdots (x-\alpha^{2s-1}) 134 | % $$ 135 | % 136 | % 137 | % THIS part is not coherent with the 'Encoding' section 138 | % We define 139 | % $$b(x) = x^{2s} \cdot m(x) \pmod{g(x)}$$ 140 | % 141 | % thus, for some $q(x)$, 142 | % $$x^{2s} m(x) = q(x) g(x) + b(x)$$ 143 | % 144 | % Define the codeword $c(x)$ as 145 | % $$c(x) = x^{2s} \cdot m(x) - b(x)$$ 146 | % UNTIL HERE. 147 | 148 | % notice that $c(x)=q(x)g(x)$, thus $c(x)$ is a multiple of $g(x)$. 149 | % 150 | % In order to check if a received codeword $c'(x)$ is correct, we'll check its divisibility by $g(x)$. If the check passes, we extract the first $k$ elements of $c'(x)$. 151 | 152 | 153 | \subsubsection{Encoding} 154 | 155 | Let $g(x)$ be the generator polynomial 156 | $$g(x) = (x-\alpha) (x-\alpha^2) \cdots (x-\alpha^{2s-1})$$ 157 | 158 | with $\alpha$ being a primitive element of $GF(p^r)$. 159 | 160 | The \emph{encoder} wants to map the message $\{ m_0, m_1, \ldots, m_{k-1} \}$ into a polynomial $p(x)$ of degree $ 5 | { 6 | \usetheme{Frankfurt} 7 | \usecolortheme{dove} %% grey scale 8 | \useinnertheme{circles} 9 | \setbeamercovered{transparent} 10 | } 11 | 12 | \hypersetup{ 13 | colorlinks, 14 | citecolor=black, 15 | filecolor=black, 16 | linkcolor=black, 17 | urlcolor=blue 18 | } 19 | \usepackage{graphicx} 20 | \usepackage{pgf-umlsd} % diagrams 21 | 22 | \setbeamertemplate{itemize}{$\circ$} 23 | \setbeamertemplate{itemize items}{$\circ$} 24 | 25 | \beamertemplatenavigationsymbolsempty %% no navigation bar 26 | 27 | \setbeamertemplate{footline}{\hspace*{.1cm}\scriptsize{ 28 | \hspace*{50pt} \hfill\insertframenumber/\inserttotalframenumber\hspace*{.1cm}\vspace*{.1cm}}} 29 | 30 | \setbeamertemplate{caption}[numbered] 31 | \setbeamerfont{caption}{size=\tiny} 32 | 33 | % message between threads. From https://tex.stackexchange.com/a/174765 34 | % Example: 35 | % \bloodymess[delay]{sender}{message content}{receiver}{DIR}{start note}{end note} 36 | \newcommand{\bloodymess}[7][0]{ 37 | \stepcounter{seqlevel} 38 | \path 39 | (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess from) {}; 40 | \addtocounter{seqlevel}{#1} 41 | \path 42 | (#4)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess to) {}; 43 | \draw[->,>=angle 60] (mess from) -- (mess to) node[midway, above] 44 | {#3}; 45 | 46 | \if R#5 47 | \node (\detokenize{#3} from) at (mess from) {\llap{#6~}}; 48 | \node (\detokenize{#3} to) at (mess to) {\rlap{~#7}}; 49 | \else\if L#5 50 | \node (\detokenize{#3} from) at (mess from) {\rlap{~#6}}; 51 | \node (\detokenize{#3} to) at (mess to) {\llap{#7~}}; 52 | \else 53 | \node (\detokenize{#3} from) at (mess from) {#6}; 54 | \node (\detokenize{#3} to) at (mess to) {#7}; 55 | \fi 56 | \fi 57 | } 58 | 59 | 60 | 61 | %Information to be included in the title page: 62 | \title{HyperNova's multifolding overview} 63 | \author{} 64 | \date{\scriptsize{2023-06-22\\\href{https://0xparc.org}{0xPARC} Novi team}} 65 | 66 | \begin{document} 67 | 68 | \frame{\titlepage} 69 | 70 | \section[Overview]{Overview} 71 | 72 | \begin{frame}{Multifolding - Overview} 73 | 74 | \begin{tiny} 75 | \begin{enumerate} 76 | \item[1.] $V \rightarrow P: \gamma \in^R \mathbb{F},~ \beta \in^R \mathbb{F}^s$ 77 | \item[2.] $V: r_x' \in^R \mathbb{F}^s$ 78 | \item[3.] $V \leftrightarrow P$: sum-check protocol: 79 | $c \leftarrow \langle P, V(r_x') \rangle (g, s, d+1, \underbrace{\sum_{j \in [t]} \gamma^j \cdot v_j}_\text{T})$, where: 80 | \begin{align*} 81 | g(x) &:= \underbrace{\left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right)}_\text{LCCCS check} + \underbrace{\gamma^{t+1} \cdot Q(x)}_\text{CCCS check}\\ 82 | L_j(x) &:= \widetilde{eq}(r_x, x) \cdot \left( 83 | \underbrace{\sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_1(y)}_\text{LCCCS check} 84 | \right)\\ 85 | Q(x) := &\widetilde{eq}(\beta, x) \cdot \left( 86 | \underbrace{ \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_2(y) \right) }_\text{CCCS check} 87 | \right) 88 | \end{align*} 89 | \end{enumerate} 90 | \end{tiny} 91 | 92 | \end{frame} 93 | 94 | \begin{frame}{Multifolding - Overview} 95 | 96 | \begin{tiny} 97 | \begin{enumerate} 98 | \item[4.] $P \rightarrow V$: $\left( (\sigma_1, \ldots, \sigma_t), (\theta_1, \ldots, \theta_t) \right)$, where $\forall j \in [t]$, 99 | $$\sigma_j = \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_1(y)$$ 100 | $$\theta_j = \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_2(y)$$ 101 | \item[5.] V: $e_1 \leftarrow \widetilde{eq}(r_x, r_x')$, $e_2 \leftarrow \widetilde{eq}(\beta, r_x')$\\ 102 | check: 103 | $$c = \left(\sum_{j \in [t]} \gamma^j \cdot e_1 \cdot \sigma_j \right) + \gamma^{t+1} \cdot e_2 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right)$$ 104 | \item[6.] $V \rightarrow P: \rho \in^R \mathbb{F}$ 105 | \item[7.] $V, P$: output the folded LCCCS instance $(C', u', \mathsf{x}', r_x', v_1', \ldots, v_t')$, where $\forall i \in [t]$: 106 | \begin{align*} 107 | C' &\leftarrow C_1 + \rho \cdot C_2\\ 108 | u' &\leftarrow u + \rho \cdot 1\\ 109 | \mathsf{x}' &\leftarrow \mathsf{x}_1 + \rho \cdot \mathsf{x}_2\\ 110 | v_i' &\leftarrow \sigma_i + \rho \cdot \theta_i 111 | \end{align*} 112 | \item[8.] $P$: output folded witness and the folded $r_w'$: 113 | \begin{align*} 114 | \widetilde{w}' &\leftarrow \widetilde{w}_1 + \rho \cdot \widetilde{w}_2\\ 115 | r_w' &\leftarrow r_{w_1} + \rho \cdot r_{w_2} 116 | \end{align*} 117 | \end{enumerate} 118 | \end{tiny} 119 | 120 | \end{frame} 121 | 122 | 123 | 124 | \begin{frame}{Multifolding - Overview} 125 | \begin{tiny} 126 | \begin{center} 127 | \begin{sequencediagram} 128 | \newinst[1]{p}{Prover} 129 | \newinst[3]{v}{Verifier} 130 | 131 | \bloodymess[1]{v}{$\gamma,~\beta,~r_x'$}{p}{L}{ 132 | \shortstack{ 133 | $\gamma \in \mathbb{F},~ \beta \in \mathbb{F}^s$\\ 134 | $r_x' \in \mathbb{F}^s$ 135 | } 136 | }{} 137 | \bloodymess[1]{p}{$c,~ \pi_{SC}$}{v}{R}{sum-check prove}{sum-check verify} 138 | \bloodymess[1]{p}{$\{\sigma_j\},~\{\theta_j\}$}{v}{R}{compute $\{\sigma_j\}, \{\theta_j\}~ \forall j \in [t]$}{verify $c$ with $\{\sigma_j\}, \{\theta_j\}$ relation} 139 | \bloodymess[1]{v}{$\rho$}{p}{L}{$\rho \in^R \mathbb{F}$}{} 140 | \callself[0]{p}{fold LCCCS instance}{p} 141 | \prelevel 142 | \callself[0]{v}{fold LCCCS instance}{v} 143 | \callself[0]{p}{fold $\widetilde{w}, r_w$}{p} 144 | \end{sequencediagram} 145 | \end{center} 146 | \end{tiny} 147 | \end{frame} 148 | 149 | 150 | \section[Checks]{Checks} 151 | 152 | \begin{tiny} 153 | \begin{frame}{LCCCS checks} 154 | $$ 155 | \color{gray}{g(x) :=} 156 | \color{black}{\underbrace{\left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right)}_\text{LCCCS} } 157 | \color{gray}{+ \gamma^{t+1} \cdot Q(x)} 158 | $$ 159 | $$ 160 | L_j(x) := \widetilde{eq}(r_x, x) \cdot \left( 161 | \underbrace{\sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_1(y)}_\text{LCCCS check} 162 | \right) 163 | $$ 164 | 165 | 166 | Notice that, $v_j$ from LCCCS relation check 167 | 168 | \begin{align*} 169 | v_j &= \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x, y) \cdot \widetilde{z}_1(y)\\ 170 | &= \sum_{x \in \{0,1\}^s} 171 | \widetilde{eq}(r_x, x) \cdot \left( \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_1(y) \right)\\ 172 | &= \sum_{x \in \{0,1\}^s} L_j(x) 173 | \end{align*} 174 | 175 | \end{frame} 176 | \begin{frame}{CCCS checks} 177 | $$ 178 | \color{gray}{g(x) := \left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right) +} 179 | \color{black}{\underbrace{\gamma^{t+1} \cdot Q(x)}_\text{CCCS}} 180 | $$ 181 | $$Q(x) := \widetilde{eq}(\beta, x) \cdot \left( 182 | \underbrace{ \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_2(y) \right) }_\text{CCCS check} 183 | \right)$$ 184 | 185 | Recall that Spartan's $\widetilde{F}_{io}(x)$ here is $q(x)$, so we're doing the same Spartan check: 186 | 187 | $$ 188 | 0 =G(\beta) = \sum_{x \in \{0,1\}^s} Q(x) = \sum_{x \in \{0,1\}^s} eq(\beta, x) \cdot q(x)$$ 189 | $$= \sum_{x \in \{0,1\}^s} 190 | \underbrace{\widetilde{eq}(\beta , x) \cdot 191 | \overbrace{ 192 | \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_2(y) \right) 193 | }^{q(x)} 194 | }_{Q(x)} 195 | $$ 196 | 197 | \end{frame} 198 | 199 | 200 | \begin{frame}{Verifier checks} 201 | \textcolor{gray}{ 202 | Recall: 203 | $$g(x) := \left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right) + \gamma^{t+1} \cdot Q(x)$$ 204 | $$c = \left(\sum_{j \in [t]} \gamma^j \cdot e_1 \cdot \sigma_j \right) + \gamma^{t+1} \cdot e_2 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right)$$ 205 | } 206 | 207 | We can see now that V's check in step 5, 208 | 209 | \begin{align*} 210 | c &= 211 | \left( \sum_{j \in [t]} \gamma^j \cdot \overbrace{e_1 \cdot \sigma_j}^{L_j(r_x')} \right) + \gamma^{t+1} \cdot \overbrace{e_2 \cdot \sum_{i \in [q]} c_i \prod_{j \in S_i} \theta_j}^{Q(x)}\\ 212 | &= \left( \sum_{j \in [t]} \gamma^j \cdot L_j(r_x') \right) + \gamma^{t+1} \cdot Q(r_x')\\ 213 | &= g(r_x') 214 | \end{align*} 215 | 216 | where $e_1 = \widetilde{eq}(r_x, r_x')$, $e_2=\widetilde{eq}(\beta, r_x')$. 217 | \end{frame} 218 | \end{tiny} 219 | 220 | \section[Multiple instances]{Multiple instances} 221 | 222 | \begin{footnotesize} 223 | \begin{frame}{Multifolding multiple instances} 224 | Hypernova paper: $\mu=1, \nu=1$ \emph{(ie. 1 LCCCS instance and 1 CCCS instance)} 225 | 226 | \vspace{1cm} 227 | In next slides 228 | \begin{itemize} 229 | \item example with: $\color{orange}{LCCCS: \mu = 2},~ \color{blue}{CCCS: \nu = 2}$ 230 | \item generalized equations for $\mu,~\nu$ 231 | \end{itemize} 232 | 233 | Let $z_1,~ \color{orange}{z_2}$ be the two LCCCS instances, and $z_3,~ \color{blue}{z_4}$ be the two CCCS instances 234 | 235 | \end{frame} 236 | \end{footnotesize} 237 | 238 | \begin{tiny} 239 | \begin{frame} 240 | In \emph{step 3}, 241 | 242 | \begin{align*} 243 | g(x) &:= \left( \sum_{j \in [t]} \gamma^j \cdot L_{1,j}(x) + \textcolor{orange}{\gamma^{t+j} \cdot L_{2,j}(x)} \right) 244 | + \gamma^{2t+1} \cdot Q_1(x) + \textcolor{cyan}{\gamma^{2t+2} \cdot Q_2(x)} \\ 245 | &L_{1,j}(x) := \widetilde{eq}(r_{1,x}, x) \cdot \left( 246 | \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_1(y) 247 | \right)\\ 248 | &\textcolor{orange}{L_{2,j}(x)} := \widetilde{eq}(\textcolor{orange}{r_{2,x}}, x) \cdot \left( 249 | \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \textcolor{orange}{\widetilde{z}_2(y)} 250 | \right)\\ 251 | &Q_1(x) := \widetilde{eq}(\beta, x) \cdot \left( 252 | \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_3(y) \right)\right)\\ 253 | &\textcolor{cyan}{Q_2(x)} := \widetilde{eq}(\textcolor{cyan}{\beta}, x) \cdot \left( 254 | \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \textcolor{cyan}{\widetilde{z}_4(y)} \right)\right) 255 | \end{align*} 256 | 257 | \framebox{\begin{minipage}{4.3 in} 258 | A generic definition of $g(x)$ for $\mu>1~\nu>1$, would be 259 | 260 | $$ 261 | g(x) := \left( \sum_{i \in [\mu]} \left( \sum_{j \in [t]} \gamma^{i \cdot t+j} \cdot L_{i,j}(x) \right) \right) 262 | + \left( \sum_{i \in [\nu]} \gamma^{\mu \cdot t + i} \cdot Q_i(x) \right) 263 | $$ 264 | \end{minipage}} 265 | 266 | Recall, the original $g(x)$ definition was 267 | $$\textcolor{gray}{g(x) := \left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right) + \gamma^{t+1} \cdot Q(x)}$$ 268 | 269 | 270 | \end{frame} 271 | 272 | \begin{frame} 273 | In \emph{step 4}, $P \rightarrow V$: $(\{\sigma_{1,j}\}, \textcolor{orange}{\{\sigma_{2,j}\}}, \{\theta_{1,j}\}, \textcolor{cyan}{\{\theta_{2,j}\}}),~ \text{where} ~\forall j \in [t]$, 274 | 275 | $$\sigma_{1,j} = \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_1(y)$$ 276 | $$\textcolor{orange}{\sigma_{2,j}} = \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \textcolor{orange}{\widetilde{z}_2(y)}$$ 277 | $$\theta_{1,j} = \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_3(y)$$ 278 | $$\textcolor{cyan}{\theta_{2,j}} = \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \textcolor{cyan}{\widetilde{z}_4(y)}$$ 279 | 280 | \framebox{\begin{minipage}{4.3 in} 281 | so in a generic way,\\ 282 | $P \rightarrow V$: 283 | $(\{\sigma_{i,j}\}, \{\theta_{k,j}\}),~ \text{where} ~\forall~ j \in [t],~ \forall~ i \in [\mu],~ \forall~ k \in [\nu]$ 284 | where 285 | $$\sigma_{i,j} = \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_i(y)$$ 286 | $$\theta_{k,j} = \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_{\mu+k}(y)$$ 287 | \end{minipage}} 288 | 289 | \end{frame} 290 | 291 | \begin{frame} 292 | And in \emph{step 5}, $V$ checks 293 | 294 | \begin{align*} 295 | c &= \left(\sum_{j \in [t]} \gamma^j \cdot e_1 \cdot \sigma_{1,j} 296 | ~\textcolor{orange}{+ \gamma^{t+j} \cdot e_2 \cdot \sigma_{2,j}}\right) 297 | + \gamma^{2t+1} \cdot e_3 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right) 298 | + \textcolor{cyan}{\gamma^{2t+2} \cdot e_4 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right)} 299 | \end{align*} 300 | 301 | where 302 | $e_1 \leftarrow \widetilde{eq}(r_{1,x}, r_x'),~ e_2 \leftarrow \widetilde{eq}(r_{2,x}, r_x')$, $e_3, e_4 \leftarrow \widetilde{eq}(\beta, r_x')$. 303 | 304 | \vspace{0.5cm} 305 | 306 | \framebox{\begin{minipage}{4.3 in} 307 | A generic definition of the check would be 308 | $$ 309 | c = \sum_{i \in [\mu]} \left(\sum_{j \in [t]} \gamma^{i \cdot t + j} \cdot e_i \cdot \sigma_{i,j} \right) \\ 310 | + \sum_{k \in [\nu]} \gamma^{\mu \cdot t+k} \cdot e_k \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_{k,j} \right) 311 | $$ 312 | \end{minipage}} 313 | 314 | where the original check was\\ 315 | $\textcolor{gray}{c = \left(\sum_{j \in [t]} \gamma^j \cdot e_1 \cdot \sigma_j \right) + \gamma^{t+1} \cdot e_2 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right)}$ 316 | 317 | \end{frame} 318 | 319 | \begin{frame} 320 | And for the \emph{step 7}, 321 | \begin{align*} 322 | C' &\leftarrow C_1 + \rho \cdot C_2 + \rho^2 C_3 + \rho^3 C_4 + \ldots = \sum_{i \in [\mu + \nu]} \rho^i \cdot C_i \\ 323 | u' &\leftarrow \sum_{i \in [\mu]} \rho^i \cdot u_i + \sum_{i \in [\nu]} \rho^{\mu + i-1} \cdot 1\\ 324 | \mathsf{x}' &\leftarrow \sum_{i \in [\mu+\nu]} \rho^i \cdot \mathsf{x}_i\\ 325 | v_i' &\leftarrow \sum_{i \in [\mu]} \rho^i \cdot \sigma_i + \sum_{i \in [\nu]} \rho^{\mu + i-1} \cdot \theta_i\\ 326 | \end{align*} 327 | 328 | and \emph{step 8}, 329 | \begin{align*} 330 | \widetilde{w}' &\leftarrow \sum_{i \in [\mu+\nu]} \rho^i\cdot \widetilde{w}_i\\ 331 | r_w' &\leftarrow \sum_{i \in [\mu+\nu]} \rho^i \cdot r_{w_i}\\ 332 | \end{align*} 333 | 334 | \end{frame} 335 | 336 | \end{tiny} 337 | 338 | \section[Wrappup]{Wrappup} 339 | 340 | \begin{frame} 341 | \frametitle{Wrappup} 342 | \begin{itemize} 343 | \item HyperNova: \href{https://eprint.iacr.org/2023/573}{https://eprint.iacr.org/2023/573} 344 | \item multifolding PoC on arkworks: \href{https://github.com/privacy-scaling-explorations/multifolding-poc}{github.com/privacy-scaling-explorations/multifolding-poc} 345 | \end{itemize} 346 | 347 | \vspace{2cm} 348 | \tiny{ 349 | $$\text{2023-06-22}$$ 350 | $$\text{\href{https://0xparc.org}{0xPARC} Novi team}$$ 351 | } 352 | \end{frame} 353 | 354 | \end{document} 355 | -------------------------------------------------------------------------------- /notes_nova.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{mathtools} 7 | \usepackage{enumerate} 8 | \usepackage{hyperref} 9 | \usepackage{xcolor} 10 | 11 | \usepackage{pgf-umlsd} % diagrams 12 | % message between threads 13 | % Example: 14 | % \bloodymess[delay]{sender}{message content}{receiver}{DIR}{start note}{end note} 15 | \newcommand{\bloodymess}[7][0]{ 16 | \stepcounter{seqlevel} 17 | \path 18 | (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess from) {}; 19 | \addtocounter{seqlevel}{#1} 20 | \path 21 | (#4)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess to) {}; 22 | \draw[->,>=angle 60] (mess from) -- (mess to) node[midway, above] 23 | {#3}; 24 | 25 | \if R#5 26 | \node (\detokenize{#3} from) at (mess from) {\llap{#6~}}; 27 | \node (\detokenize{#3} to) at (mess to) {\rlap{~#7}}; 28 | \else\if L#5 29 | \node (\detokenize{#3} from) at (mess from) {\rlap{~#6}}; 30 | \node (\detokenize{#3} to) at (mess to) {\llap{#7~}}; 31 | \else 32 | \node (\detokenize{#3} from) at (mess from) {#6}; 33 | \node (\detokenize{#3} to) at (mess to) {#7}; 34 | \fi 35 | \fi 36 | } 37 | 38 | % prevent warnings of underfull \hbox: 39 | \usepackage{etoolbox} 40 | \apptocmd{\sloppy}{\hbadness 4000\relax}{}{} 41 | 42 | \theoremstyle{definition} 43 | \newtheorem{definition}{Def}[section] 44 | \newtheorem{theorem}[definition]{Thm} 45 | 46 | % custom lemma environment to set custom numbers 47 | \newtheorem{innerlemma}{Lemma} 48 | \newenvironment{lemma}[1] 49 | {\renewcommand\theinnerlemma{#1}\innerlemma} 50 | {\endinnerlemma} 51 | 52 | 53 | \title{Notes on Nova} 54 | \author{arnaucube} 55 | \date{March 2023} 56 | 57 | \begin{document} 58 | 59 | \maketitle 60 | 61 | \begin{abstract} 62 | Notes taken while reading Nova \cite{cryptoeprint:2021/370} paper. 63 | 64 | Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 65 | 66 | The notes are not complete, don't include all the steps neither all the proofs. 67 | 68 | Thanks to \href{https://twitter.com/levs57}{Levs57}, \href{https://twitter.com/nibnalin}{Nalin Bhardwaj} and \href{https://twitter.com/cperezz19}{Carlos Pérez} for clarifications on the Nova paper. 69 | \end{abstract} 70 | 71 | \tableofcontents 72 | 73 | \section{NIFS} 74 | 75 | \subsection{R1CS modification} 76 | 77 | \paragraph{R1CS} 78 | R1CS instance: $(A, B, C, io, m, n)$, where $io$ denotes the public input and output, $A, B, C \in \mathbb{F}^{m \times n}$, with $m \geq |io|+1$. 79 | R1CS is satisfied by a witness $w \in \mathbb{F}^{m-|io|-1}$ such that 80 | $$Az \circ Bz = Cz$$ 81 | where $z=(io, 1, w)$. 82 | 83 | \vspace{0.5cm} 84 | 85 | \textbf{Want}: merge 2 instances of R1CS with the same matrices into a single one. Each instance has $z_i = (W_i,~ x_i)$ (public witness, private values resp.). 86 | 87 | \paragraph{traditional R1CS} 88 | Merged instance with $z=z_1 + r z_2$, for rand $r$. But, since R1CS is not linear $\longrightarrow$ can not apply. 89 | 90 | eg. 91 | \begin{align*} 92 | Az \circ Bz &= A(z_1 + r z_2) \circ B (z_1 + r z_2)\\ 93 | &= A z_1 \circ B z_1 + r(A z_1 \circ B z_2 + A z_2 \circ B z_1) + r^2 (A z_2 \circ B z_2)\\ 94 | &\neq Cz 95 | \end{align*} 96 | 97 | $\longrightarrow$ introduce error vector $E \in \mathbb{F}^m$, which absorbs the cross-temrs generated by folding. 98 | 99 | $\longrightarrow$ introduce scalar $u$, which absorbs an extra factor of $r$ in $C z_1 + r^2 C z_2$ and in $z=(W, x, 1+r\cdot 1)$. 100 | 101 | \paragraph{Relaxed R1CS} 102 | \begin{align*} 103 | &u=u_1+r u_2\\ 104 | &E=E_1 + r (A z_1 \circ B z_2 + A z_2 \circ B z_1 - u_1 C z_2 - u_2 C z_1) + r^2 E_2\\ 105 | &Az \circ Bz = uCz + E,~~ with~ z=(W,~x,~u) 106 | \end{align*} 107 | where R1CS set $E=0,~u=1$. 108 | 109 | \begin{align*} 110 | Az \circ Bz &= A z_1 \circ B z_1 + r(A z_1 \circ B z_2 + A z_2 \circ B z_1) + r^2 (A z_2 \circ B z_2)\\ 111 | &= (u_1 C z_1 + E_1) + r (A z_1 \circ B z_2 + A z_2 \circ B z_1) + r^2 (u_2 C z_2 + E_2)\\ 112 | &= u_1 C z_1 + \underbrace{E_1 + r(A z_1 \circ B z_2 + A z_2 \circ B z_1) + r^2 E_2}_\text{E} + r^2 u_2 C z_2\\ 113 | &= u_1 C z_1 + r^2 u_2 C z_2 + E\\ 114 | &= (u_1 + r u_2) \cdot C \cdot (z_1 + r z_2) + E\\ 115 | &= uCz + E 116 | \end{align*} 117 | 118 | For R1CS matrices $(A,~B,~C)$, the folded witness $W$ is a satisfying witness for the folded instance $(E,~u,~x)$. 119 | 120 | 121 | 122 | \vspace{20px} 123 | Problem: not non-trivial, and not zero-knowledge. Solution: use polynomial commitment with hiding, binding, succintness and additively homomorphic properties. 124 | 125 | \paragraph{Committed Relaxed R1CS} 126 | Instance for a Committed Relaxed R1CS\\ 127 | $(\overline{E}, u, \overline{W}, x)$, satisfied by a witness $(E, r_E, W, r_W)$ such that 128 | \begin{align*} 129 | &\overline{E} = Com(E, r_E)\\ 130 | &\overline{W} = Com(E, r_W)\\ 131 | &Az \circ Bz = uCz+E,~~ where~z=(W, x, u) 132 | \end{align*} 133 | 134 | 135 | \subsection{Folding scheme for committed relaxed R1CS} 136 | 137 | V and P take two \emph{committed relaxed R1CS} instances 138 | \begin{align*} 139 | \varphi_1&=(\overline{E}_1, u_1, \overline{W}_1, x_1)\\ 140 | \varphi_2&=(\overline{E}_2, u_2, \overline{W}_2, x_2) 141 | \end{align*} 142 | 143 | P additionally takes witnesses to both instances 144 | \begin{align*} 145 | (E_1, r_{E_1}, W_1, r_{W_1})\\ 146 | (E_2, r_{E_2}, W_2, r_{W_2}) 147 | \end{align*} 148 | 149 | Let $Z_1 = (W_1, x_1, u_1)$ and $Z_2 = (W_2, x_2, u_2)$. 150 | 151 | % \paragraph{Protocol} 152 | \begin{enumerate} 153 | \item P send $\overline{T} = Com(T, r_T)$,\\ 154 | where $T=A z_1 \circ B z_1 + A z_2 \circ B z_2 - u_1 C z_1 - u_2 C z_2$\\ 155 | and rand $r_T \in \mathbb{F}$ 156 | \item V sample random challenge $r \in \mathbb{F}$ 157 | \item V, P output the folded instance $\varphi = (\overline{E}, u, \overline{W}, x)$ 158 | \begin{align*} 159 | &\overline{E}=\overline{E}_1 + r \overline{T} + r^2 \overline{E}_2\\ 160 | &u = u_1 + r u_2\\ 161 | &\overline{W} = \overline{W}_1 + r \overline{W}_2\\ 162 | &x = x_1 + r x_2 163 | \end{align*} 164 | \item P outputs the folded witness $(E, r_E, W, r_W)$ 165 | \begin{align*} 166 | &E = E_1 + r T + r^2 E_2\\ 167 | &r_E = r_{E_1} + r \cdot r_T + r^2 r_{E_2}\\ 168 | &W=W_1 + r W_2\\ 169 | &r_W = r_{W_1} + r \cdot r_{W_2} 170 | \end{align*} 171 | \end{enumerate} 172 | 173 | P will prove that knows the valid witness $(E, r_E, W, r_W)$ for the committed relaxed R1CS without revealing its value. 174 | 175 | \begin{center} 176 | \begin{sequencediagram} 177 | \newinst[1]{p}{Prover} 178 | \newinst[3]{v}{Verifier} 179 | 180 | \bloodymess[1]{p}{$\overline{T}$}{v}{R}{ 181 | \shortstack{ 182 | $T=A z_1 \circ B z_1 + A z_2 \circ B z_2 - u_1 C z_2 - u_2 C z_2$\\ 183 | $\overline{T}=Commit(T, r_T)$ 184 | } 185 | }{ 186 | \shortstack{ 187 | $r \in^R \mathbb{F}_p$\\ 188 | $\overline{E} = \overline{E}_1 + r \overline{T} + r^2 \overline{E}_2$\\ 189 | $u= u_1 + r u_2$\\ 190 | $\overline{W} = \overline{W}_1 + r \overline{W}_2$\\ 191 | $\overline{x} = \overline{x}_1 + r \overline{x}_2$\\ 192 | $\varphi=(\overline{E}, u, \overline{W}, x)$ 193 | } 194 | } 195 | \bloodymess[1]{v}{$r$}{p}{L}{}{ 196 | \shortstack{ 197 | $E = E_1 + r T + r^2 E_2$\\ 198 | $u= u_1 + r u_2$\\ 199 | $W = W_1 + r W_2$\\ 200 | $r_{W} = r_{W_1} + r r_{W_2}$\\ 201 | $(E, r_E, W, r_W)$ 202 | } 203 | } 204 | \end{sequencediagram} 205 | \end{center} 206 | 207 | 208 | The previous protocol achieves non-interactivity via Fiat-Shamir transform, obtaining a \emph{Non-Interactive Folding Scheme for Committed Relaxed R1CS}. 209 | 210 | Note: the paper later uses $\mathsf{u}_i,~ \mathsf{U}_i$ for the two inputted $\varphi_1,~ \varphi_2$, and later $\mathsf{u}_{i+1}$ for the outputted $\varphi$. Also, the paper later uses $\mathsf{w},~ \mathsf{W}$ to refer to the witnesses of two folded instances (eg. $\mathsf{w}=(E, r_E, W, r_W)$). 211 | 212 | 213 | \subsection{NIFS} 214 | 215 | \underline{fold witness, $(pk, (u_1, w_1), (u_2, w_2))$}: 216 | \begin{enumerate} 217 | \item $T=A z_1 \circ B z_1 + A z_2 \circ B z_2 - u_1 C z_2 - u_2 C z_2$ 218 | \item $\overline{T}=Commit(T, r_T)$ 219 | % \item output the folded instance $\varphi = (\overline{E}, u, \overline{W}, x)$ 220 | % \begin{align*} 221 | % &\overline{E}=\overline{E}_1 + r \overline{T} + r^2 \overline{E}_2\\ 222 | % &u = u_1 + r u_2\\ 223 | % &\overline{W} = \overline{W}_1 + r \overline{W}_2\\ 224 | % &x = x_1 + r x_2 225 | % \end{align*} 226 | \item output the folded witness $(E, r_E, W, r_W)$ 227 | \begin{align*} 228 | &E = E_1 + r T + r^2 E_2\\ 229 | &r_E = r_{E_1} + r \cdot r_T + r^2 r_{E_2}\\ 230 | &W=W_1 + r W_2\\ 231 | &r_W = r_{W_1} + r \cdot r_{W_2} 232 | \end{align*} 233 | \end{enumerate} 234 | 235 | \underline{fold instances $(\varphi_1, \varphi_2) \rightarrow \varphi$, $(vk, u_1, u_2, \overline{E}_1, \overline{E}_2, \overline{W}_1, \overline{W}_2, \overline{T})$}:\\ 236 | V compute folded instance $\varphi = (\overline{E}, u, \overline{W}, x)$ 237 | \begin{align*} 238 | &\overline{E}=\overline{E}_1 + r \overline{T} + r^2 \overline{E}_2\\ 239 | &u = u_1 + r u_2\\ 240 | &\overline{W} = \overline{W}_1 + r \overline{W}_2\\ 241 | &x = x_1 + r x_2 242 | \end{align*} 243 | 244 | \section{Nova} 245 | IVC (Incremental Verifiable Computation) scheme for a non-interactive folding scheme. 246 | 247 | \subsection{IVC proofs} 248 | 249 | Allows prover to show $z_n = F^{(n)}(z_0)$, for some count $n$, initial input $z_0$, and output $z_n$.\\ 250 | $F$: program function (polynomial-time computable)\\ 251 | $F'$: augmented function, invokes $F$ and additionally performs fold-related stuff. 252 | 253 | \vspace{0.5cm} 254 | Two committed relaxed R1CS instances:\\ 255 | $\mathsf{U}_i$: represents the correct execution of invocations $1, \ldots, i-1$ of $F'$\\ 256 | $\mathsf{u}_i$: represents the correct execution of invocations $i$ of $F'$ 257 | 258 | \paragraph{Simplified version of $F'$ for intuition} 259 | \vspace{0.5cm} 260 | $F'$ performs two tasks: 261 | \begin{enumerate}[i.] 262 | \item execute a step of the incremental computation: 263 | instance $\mathsf{u}_i$ contains $z_i$, used to output $z_{i+1}=F(z_i)$ 264 | \item invokes the verifier of the non-interactive folding scheme to fold the task of checking $\mathsf{u}_i$ and $\mathsf{U}_i$ into the task of checking a single instance $\mathsf{U}_{i+1}$ 265 | \end{enumerate} 266 | 267 | \vspace{0.5cm} 268 | $F'$ proves that: 269 | \begin{enumerate} 270 | \item $\exists ( (i, z_0, z_i, \mathsf{u}_i, \mathsf{U}_i), \mathsf{U}_{i+1}, \overline{T})$ such that 271 | \begin{enumerate}[i.] 272 | \item $\mathsf{u}_i.x = H(vk, i, z_0, z_i, \mathsf{U}_i)$ 273 | \item $h_{i+1} = H(vk, i+1, z_0, F(z_i), \mathsf{U}_{i+1})$ 274 | \item $\mathsf{U}_{i+1} = NIFS.V(vk, \mathsf{U}_i, \mathsf{u}_i, \overline{T})$ 275 | \end{enumerate} 276 | \item $F'$ outputs $h_{i+1}$ 277 | \end{enumerate} 278 | 279 | 280 | $F'$ is described as follows:\\ 281 | \underline{$F'(vk, \mathsf{U}_i, \mathsf{u}_i, (i, z_0, z_i), w_i, \overline{T}) \rightarrow x$}:\\ 282 | if $i=0$, output $H(vk, 1, z_0, F(z_0, w_i), \mathsf{u}_{\bot})$\\ 283 | otherwise 284 | \begin{enumerate} 285 | \item check $\mathsf{u}_i.x = H(vk, i, z_0, z_i, \mathsf{U}_i)$ 286 | \item check $(\mathsf{u}_i.\overline{E}, \mathsf{u}_i.u) = (\mathsf{u}_{\bot}.\overline{E}, 1)$ 287 | \item compute $\mathsf{U}_{i+1} \leftarrow NIFS.V(vk, U, u, \overline{T})$ 288 | \item output $H(vk, i+1, z_0, F(z_i, w_i), \mathsf{U}_{i+1})$ 289 | \end{enumerate} 290 | 291 | % TODO add diagram 292 | 293 | \paragraph{IVC Proof} 294 | iteration $i+1$: prover runs $F'$ and computes $\mathsf{u}_{i+1},~ \mathsf{U}_{i+1}$, with corresponding witnesses $\mathsf{w}_{i+1},~ \mathsf{W}_{i+1}$. 295 | $(\mathsf{u}_{i+1},~ \mathsf{U}_{i+1})$ attest correctness of $i+1$ invocations of $F'$, the IVC proof is $\pi_{i+1} = ( (\mathsf{U}_{i+1}, \mathsf{W}_{i+1}), (\mathsf{u}_{i+1}, \mathsf{w}_{i+1}))$. 296 | 297 | 298 | \vspace{0.5cm} 299 | 300 | \underline{$P(pk, (i, z_0, z_i), \mathsf{w}_i, \pi_i) \rightarrow \pi_{i+1}$}:\\ 301 | Parse $\pi_i = ( (\mathsf{U}_i, \mathsf{W}_i), (\mathsf{u}_i, \mathsf{w}_i))$, then 302 | \begin{enumerate} 303 | \item if $i=0$: $(\mathsf{U}_{i+1}, \mathsf{W}_{i+1}, \overline{T}) \leftarrow (\mathsf{u}_{\perp}, \mathsf{w}_{\perp}, \mathsf{u}_{\perp}.{\overline{E}})$\\ 304 | otherwise: $(\mathsf{U}_{i+1}, \mathsf{W}_{i+1}, \overline{T}) \leftarrow NIFS.P(pk, (\mathsf{U}_i, \mathsf{W}_i), (\mathsf{u}_i, \mathsf{w}_i))$ 305 | \item compute $(\mathsf{u}_{i+1}, \mathsf{w}_{i+1}) \leftarrow trace(F', (vk, \mathsf{U}_i, \mathsf{u}_i, (i, z_0, z_i), \mathsf{w}_i, \overline{T}))$ 306 | \item output $\pi_{i+1} \leftarrow ((\mathsf{U}_{i+1}, \mathsf{W}_{i+1}), (\mathsf{u}_{i+1}, \mathsf{w}_{i+1}))$ 307 | \end{enumerate} 308 | 309 | \underline{$V(vk, (i, z_0, z_i), \pi_i) \rightarrow \{0,1\}$}: 310 | if $i=0$: check that $z_i=z_0$\\ 311 | otherwise, parse $\pi_i = ( (\mathsf{U}_i, \mathsf{W}_i), (\mathsf{u}_i, \mathsf{w}_i))$, then 312 | \begin{enumerate} 313 | \item check $\mathsf{u}_i.x = H(vk, i, z_0, z_i, \mathsf{U}_i)$ 314 | \item check $(\mathsf{u}_i.{\overline{E}}, \mathsf{u}_i.u) = (\mathsf{u}_{\perp}.{\overline{E}}, 1)$ 315 | \item check that $\mathsf{W}_i,~ \mathsf{w}_i$ are satisfying witnesses to $\mathsf{U}_i,~ \mathsf{u}_i$ respectively 316 | \end{enumerate} 317 | 318 | \vspace{0.5cm} 319 | 320 | \paragraph{A zkSNARK of a Valid IVC Proof} prover and verifier:\\ 321 | \underline{$P(pk, (i, z_0, z_i), \Pi) \rightarrow \pi$}:\\ 322 | if $i=0$, output $\perp$, otherwise:\\ 323 | parse $\Pi$ as $((\mathsf{U}, \mathsf{W}), (\mathsf{u}, \mathsf{w}))$ 324 | \begin{enumerate} 325 | \item compute $(\mathsf{U}', \mathsf{W}', \overline{T}) \leftarrow NIFS.P(pk_{NIFS}, (\mathsf{U,~W}), (\mathsf{u,~w}))$ 326 | \item compute $\pi_{\mathsf{u}'} \leftarrow zkSNARK.P(pk_{zkSNARK}, \mathsf{U}', \mathsf{W}')$ 327 | \item output $(\mathsf{U,~ u}, \overline{T}, \pi_{\mathsf{u}'})$ 328 | \end{enumerate} 329 | 330 | \underline{$V(vk, (i, z_0, z_i), \pi) \rightarrow \{0,1\}$}:\\ 331 | if $i=0$: check that $z_i=z_0$\\ 332 | parse $\pi$ as $(\mathsf{U}, \mathsf{u}, \overline{T}, \pi_{\mathsf{u}'})$ 333 | \begin{enumerate} 334 | \item check $\mathsf{u}.x = H(vk_{NIFS}, i, z_0, z_i, \mathsf{U})$ 335 | \item check $(\mathsf{u}.{\overline{E}}, \mathsf{u}.u) = (\mathsf{u}_{\perp}.{\overline{E}}, 1)$ 336 | \item compute $\mathsf{U}' \leftarrow NIFS.V(vk_{NIFS}, \mathsf{U}, \mathsf{u}, \overline{T})$ 337 | \item check $zkSNARK.V(vk_{zkSNARK}, \mathsf{U}', \pi_{\mathsf{u}'})=1$ 338 | \end{enumerate} 339 | 340 | 341 | \bibliography{paper-notes.bib} 342 | \bibliographystyle{unsrt} 343 | 344 | \end{document} 345 | -------------------------------------------------------------------------------- /slides_hypernova-part1-introduction.tex: -------------------------------------------------------------------------------- 1 | \documentclass{beamer} 2 | \usefonttheme[onlymath]{serif} 3 | 4 | \mode 5 | { 6 | \usetheme{Frankfurt} 7 | \usecolortheme{dove} %% grey scale 8 | \useinnertheme{circles} 9 | % \setbeamercovered{transparent} 10 | } 11 | 12 | \hypersetup{ 13 | colorlinks, 14 | citecolor=black, 15 | filecolor=black, 16 | linkcolor=black, 17 | urlcolor=blue 18 | } 19 | \usepackage{graphicx} 20 | \usepackage{listings} % embed code 21 | 22 | \setbeamertemplate{itemize}{$\circ$} 23 | \setbeamertemplate{itemize items}{$\circ$} 24 | 25 | \beamertemplatenavigationsymbolsempty %% no navigation bar 26 | 27 | \setbeamertemplate{footline}{\hspace*{.1cm}\scriptsize{ 28 | \hspace*{50pt} \hfill\insertframenumber/\inserttotalframenumber\hspace*{.1cm}\vspace*{.1cm}}} 29 | 30 | \setbeamertemplate{caption}[numbered] 31 | \setbeamerfont{caption}{size=\tiny} 32 | 33 | 34 | 35 | 36 | \title{HyperNova introduction} 37 | \author{} 38 | \date{\scriptsize{2023-07-25\\\href{https://0xparc.org}{0xPARC}, London}} 39 | 40 | \begin{document} 41 | 42 | \frame{\titlepage} 43 | 44 | 45 | % NOTE: This talk provides an overview, if people is interested we can do another session going more into the technical details of the schemes. 46 | 47 | 48 | \section[Preliminaries]{Preliminaries} 49 | 50 | \begin{frame}{IVC} 51 | 52 | For a function $F$, with initial input $z_0$, an IVC scheme allows a prover to produce a proof $\pi_i$ for the statement $z_i = F^{(i)}(z_0)$, given a proof $\pi_{i-1}$ for the statement $z_{i-1} = F^{(i-1)}(z_0)$ 53 | 54 | TODO add draw 55 | TODO add reference to Valiant paper (2008) 56 | 57 | \end{frame} 58 | 59 | \begin{frame}{Recursion before folding schemes} 60 | We used to use recursive SNARKs to achieve IVC. 61 | 62 | \begin{itemize} 63 | \item Prove verification in circuit: inside a circuit, verify another proof 64 | \begin{itemize} 65 | \item eg. verifying a Groth16 proof inside a Groth16 circuit. 66 | \end{itemize} 67 | \item Amortized accumulation 68 | \begin{itemize} 69 | \item eg. Halo 70 | \end{itemize} 71 | \end{itemize} 72 | \end{frame} 73 | 74 | \begin{frame}{R1CS refresher} 75 | 76 | R1CS instance: $(\{A, B, C\} \in \mathbb{F}^{m \times n},~ io,~ m,~ n,~ l)$, such that for $z=(io \in \mathbb{F}^l, 1, w \in \mathbb{F}^{m-l-1}) \in \mathbb{F}^m$, 77 | 78 | $$Az \circ Bz = Cz$$ 79 | 80 | Typically we use some scheme to prove that the previous equation is fulfilled by some private $w$ (eg. Groth16, Marlin, Spartan, etc). 81 | 82 | \end{frame} 83 | 84 | % \begin{frame}{R1CS refresher} 85 | % TODO add A, B, C example from Vitalik article 86 | % \end{frame} 87 | 88 | \begin{frame}{Random linear combination} 89 | 90 | Combine 2 instances together through a random linear comibnation, and the outputted instance will still satisfy the relation. 91 | 92 | \begin{itemize} 93 | \item Have 2 values $x_1, x_2$. 94 | \item Set $r \in^R \mathbb{F}$ 95 | \item Compute $x_3 = x_1 + r \cdot x_2$. 96 | \end{itemize} 97 | 98 | \pause 99 | 100 | Combined with homomorphic commitments 101 | \begin{itemize} 102 | \item We can do random linear combinations with the commitments and their witnesses, and the output can still be opened 103 | \end{itemize} 104 | 105 | % TODO check on internet if there is some more standard definition / examples. 106 | 107 | \end{frame} 108 | 109 | 110 | \section[Nova]{Nova} 111 | 112 | \begin{frame}{Folding schemes} 113 | We're not verifying the entire proof 114 | \begin{itemize} 115 | \item Take n instances and 'batch' them together 116 | \begin{itemize} 117 | \item Folds $k$ (eg. 2) instances (eg. R1CS instances) and their respective witnesses into a single one 118 | \end{itemize} 119 | \item At the end of the chain of folds, we just prove that the last fold is correct through a SNARK 120 | \begin{itemize} 121 | \item Which implies that all the previous folds were correct 122 | \end{itemize} 123 | \end{itemize} 124 | 125 | \pause 126 | 127 | In Nova: folding without a SNARK, we just reduce the satisfiability of the 2 inputted instances to the satisfiability of the single outputted one. 128 | 129 | [TODO image of multiple folding iterations] 130 | 131 | \end{frame} 132 | 133 | 134 | \begin{frame}{Relaxed R1CS} 135 | We work with \emph{relaxed R1CS} 136 | 137 | $$Az \circ Bz = u \cdot Cz + E$$ 138 | 139 | \begin{scriptsize} % TODO use the other simpler font syntax 140 | (= R1CS when $u=1,~ E=0$) 141 | \end{scriptsize} 142 | 143 | \begin{itemize} 144 | \item main idea: allows us to fold, but accumulates \emph{cross terms} 145 | \pause 146 | \item when we do the \emph{relaxed} of higher degree equations (eg. plonkish), the cross terms grow (eg. Sangria with higher degree gates) 147 | \end{itemize} 148 | 149 | \end{frame} 150 | 151 | \begin{frame}{NIFS - setup} 152 | V and P: \emph{committed relaxed R1CS} instances 153 | \begin{align*} 154 | \varphi_1&=(\overline{E}_1, u_1, \overline{w}_1, x_1)\\ 155 | \varphi_2&=(\overline{E}_2, u_2, \overline{w}_2, x_2) 156 | \end{align*} 157 | 158 | P: witnesses 159 | \begin{align*} 160 | (E_1, r_{E_1}, w_1, r_{w_1})\\ 161 | (E_2, r_{E_2}, w_2, r_{w_2}) 162 | \end{align*} 163 | 164 | Let $z_1 = (w_1, x_1, u_1)$ and $z_2 = (w_2, x_2, u_2)$. 165 | 166 | \end{frame} 167 | 168 | \begin{frame}{NIFS} 169 | \begin{footnotesize} 170 | % While Prover works with $w, E$, Verifier works with commitments to them (\emph{Committed Relaxed R1CS}).\\ 171 | % To keep the relations working with the random linear combinations, we use homomorphic commitments. 172 | 173 | \begin{itemize} 174 | \item V, P: folded instance $\varphi = (\overline{E}, u, \overline{w}, x)$ 175 | \begin{align*} 176 | &\overline{E}=\overline{E}_1 + r \overline{T} + r^2 \overline{E}_2\\ 177 | &u = u_1 + r u_2\\ 178 | &\overline{w} = \overline{w}_1 + r \overline{w}_2\\ 179 | &x = x_1 + r x_2 180 | \end{align*} 181 | \item P: folded witness $(E, r_E, w, r_W)$ 182 | \begin{align*} 183 | &E = E_1 + r T + r^2 E_2\\ 184 | &r_E = r_{E_1} + r \cdot r_T + r^2 r_{E_2}\\ 185 | &w=w_1 + r w_2\\ 186 | &r_W = r_{w_1} + r \cdot r_{w_2} 187 | \end{align*} 188 | \end{itemize} 189 | \end{footnotesize} 190 | \pause 191 | \begin{scriptsize} 192 | Note: $T$ are the cross-terms coming from combining the two R1CS instances from 193 | \begin{align*} 194 | Az \circ Bz &=A(z_1 + r \cdot z_2) \circ B(z_1 + r z_2)\\ 195 | &=A z_1 \circ B z_1 + r(A z_1 \circ B z_2 + A z_2 \circ B z_1) + r^2 (A z_2 \circ B z_2) = \ldots 196 | \end{align*} 197 | \end{scriptsize} 198 | 199 | \end{frame} 200 | 201 | \begin{frame}{NIFS} 202 | 203 | \begin{small} 204 | $$E=E_1 + r \underbrace{ (A z_1 \circ B z_2 + A z_2 \circ B z_1 - u_1 C z_2 - u_2 C z_1) }_\text{cross-terms} + r^2 E_2$$ 205 | \end{small} 206 | 207 | $Az \circ Bz = uCz + E$ will hold for valid $z$ (which comes from valid $z_1,~ z_2$). 208 | 209 | [TODO add image of function F' with F inside with extra checks] 210 | 211 | \end{frame} 212 | 213 | 214 | 215 | \begin{frame}{NIFS} 216 | 217 | Each fold: $2~EC_{Add} + 1~EC_{Mul} + 1~hash$ 218 | 219 | 20k R1CS constraints (using curve cycles) 220 | 221 | {\footnotesize 222 | (so folding makes sense when we have a circuit with more than $2 \cdot 20k$ constraints) 223 | } 224 | 225 | \pause 226 | After all the folding iterations, Nova generates a SNARK proving the last folding instance. 227 | 228 | In Nova implementation, they use Spartan. 229 | \end{frame} 230 | 231 | \begin{frame}{Benchmarks} 232 | 233 | % TODO: review names, and add links to profiles. 234 | Benchmarks that Oskar, Carlos, et al did during the Vietnam residency in April 235 | \href{https://hackmd.io/u3qM9s_YR1emHZSg3jteQA?view}{https://hackmd.io/u3qM9s\_YR1emHZSg3jteQA} 236 | 237 | \begin{center} 238 | \begin{tabular}{ |c|c|c| } 239 | \hline 240 | Size & Constraints & Time\\ 241 | \hline 242 | 2KB & 883k & 320ms\\ 243 | 4KB & 1.7m & 521ms\\ 244 | 8KB & 3.4m & 1s\\ 245 | 16KB & 6.8m & 1.9s\\ 246 | 32KB & 13.7m & 4.1s \\ 247 | \hline 248 | \end{tabular}\\ 249 | {\footnotesize eg. for 8kb, x100 Halo2 and Plonky2} 250 | \end{center} 251 | 252 | (this is for the folding, without the last snark) 253 | 254 | \end{frame} 255 | 256 | \begin{frame}{SuperNova} 257 | \begin{itemize} 258 | \item iteration on Nova, combining \emph{different circuits} in a single one with \emph{selectors} 259 | \item so we can work with a big circuit with \emph{subcircuits} without paying the whole size cost on each iteration 260 | \item in IVC terms: fold multiple $F_i$ in a single $F'$ (in Nova was a single $F$ in $F'$) 261 | \end{itemize} 262 | 263 | This is useful for example for a VM, doing one $F_i$ for each opcode 264 | 265 | \end{frame} 266 | 267 | \section[HyperNova]{HyperNova} 268 | 269 | % \begin{frame}{CCS} 270 | % \begin{itemize} 271 | % \item kind of a generalization of constraint systems 272 | % \item can translate R1CS,Plonk,AIR to CCS 273 | % \end{itemize} 274 | % $$\sum_{i=0}^{q-1} c_i \cdot \bigcirc_{j \in S_i} M_j \cdot z ==0$$ 275 | % \end{frame} 276 | 277 | \begin{frame}{R1CS to CCS example} 278 | 279 | \begin{scriptsize} 280 | \begin{itemize} 281 | \item Kind of a generalization of constraint systems 282 | \item Can translate R1CS,Plonk,AIR to CCS 283 | \end{itemize} 284 | \pause 285 | \begin{description} 286 | \item[CCS instance] $S_{CCS} = (m, n, N, l, t, q, d, M, S, c)$\\ 287 | where we have the same parameters than in $S_{R1CS}$, but additionally:\\ 288 | $t=|M|$, $q = |c| = |S|$, $d$= max degree in each variable. 289 | \item[R1CS-to-CCS parameters] $n=n,~ m=m,~ N=N,~ l=l,~ t=3,~ q=2,~ d=2$, $M=\{A,B,C\}$, $S=\{\{0,~1\},~ \{2\}\}$, $c=\{1,-1\}$ 290 | \end{description} 291 | \pause 292 | 293 | The CCS relation check: 294 | \end{scriptsize} 295 | 296 | $$\sum_{i=0}^{q-1} c_i \cdot \bigcirc_{j \in S_i} M_j \cdot z ==0$$ 297 | 298 | \begin{scriptsize} 299 | In our R1CS-to-CCS parameters is equivalent to 300 | \begin{align*} 301 | &c_0 \cdot ( (M_0 z) \circ (M_1 z) ) + c_1 \cdot (M_2 z) ==0\\ 302 | \Longrightarrow &1 \cdot ( (A z) \circ (B z) ) + (-1) \cdot (C z) ==0\\ 303 | \Longrightarrow &( (A z) \circ (B z) ) - (C z) ==0 304 | \end{align*} 305 | \end{scriptsize} 306 | 307 | \end{frame} 308 | 309 | 310 | \begin{frame}{Multifolding} 311 | \begin{itemize} 312 | \item Nova: 2-to-1 folding 313 | \item HyperNova: multifolding, k-to-1 folding 314 | \item We fold while through a SumCheck proving the correctness of the fold 315 | \end{itemize} 316 | 317 | SumCheck's polynomial work is trivial, most of the cost comes from Poseidon hash in the transcript 318 | 319 | [TODO WIP section] 320 | 321 | \end{frame} 322 | 323 | \begin{frame}{Multifolding - Overview} 324 | 325 | \begin{tiny} 326 | \begin{enumerate} 327 | \item[1.] $V \rightarrow P: \gamma \in^R \mathbb{F},~ \beta \in^R \mathbb{F}^s$ 328 | \item[2.] $V: r_x' \in^R \mathbb{F}^s$ 329 | \item[3.] $V \leftrightarrow P$: sum-check protocol: 330 | $c \leftarrow \langle P, V(r_x') \rangle (g, s, d+1, \underbrace{\sum_{j \in [t]} \gamma^j \cdot v_j}_\text{T})$, where: 331 | \begin{align*} 332 | g(x) &:= \underbrace{\left( \sum_{j \in [t]} \gamma^j \cdot L_j(x) \right)}_\text{LCCCS check} + \underbrace{\gamma^{t+1} \cdot Q(x)}_\text{CCCS check}\\ 333 | L_j(x) &:= \widetilde{eq}(r_x, x) \cdot \left( 334 | \underbrace{\sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_1(y)}_\text{LCCCS check} 335 | \right)\\ 336 | Q(x) := &\widetilde{eq}(\beta, x) \cdot \left( 337 | \underbrace{ \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \left( \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(x, y) \cdot \widetilde{z}_2(y) \right) }_\text{CCCS check} 338 | \right) 339 | \end{align*} 340 | \end{enumerate} 341 | \end{tiny} 342 | 343 | \end{frame} 344 | 345 | \begin{frame}{Multifolding - Overview} 346 | 347 | \begin{tiny} 348 | \begin{enumerate} 349 | \item[4.] $P \rightarrow V$: $\left( (\sigma_1, \ldots, \sigma_t), (\theta_1, \ldots, \theta_t) \right)$, where $\forall j \in [t]$, 350 | $$\sigma_j = \sum_{y \in \{0,1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_1(y)$$ 351 | $$\theta_j = \sum_{y \in \{0, 1\}^{s'}} \widetilde{M}_j(r_x', y) \cdot \widetilde{z}_2(y)$$ 352 | \item[5.] V: $e_1 \leftarrow \widetilde{eq}(r_x, r_x')$, $e_2 \leftarrow \widetilde{eq}(\beta, r_x')$\\ 353 | check: 354 | $$c = \left(\sum_{j \in [t]} \gamma^j \cdot e_1 \cdot \sigma_j \right) + \gamma^{t+1} \cdot e_2 \cdot \left( \sum_{i=1}^q c_i \cdot \prod_{j \in S_i} \theta_j \right)$$ 355 | \item[6.] $V \rightarrow P: \rho \in^R \mathbb{F}$ 356 | \item[7.] $V, P$: output the folded LCCCS instance $(C', u', \mathsf{x}', r_x', v_1', \ldots, v_t')$, where $\forall i \in [t]$: 357 | \begin{align*} 358 | C' &\leftarrow C_1 + \rho \cdot C_2\\ 359 | u' &\leftarrow u + \rho \cdot 1\\ 360 | \mathsf{x}' &\leftarrow \mathsf{x}_1 + \rho \cdot \mathsf{x}_2\\ 361 | v_i' &\leftarrow \sigma_i + \rho \cdot \theta_i 362 | \end{align*} 363 | \item[8.] $P$: output folded witness and the folded $r_w'$: 364 | \begin{align*} 365 | \widetilde{w}' &\leftarrow \widetilde{w}_1 + \rho \cdot \widetilde{w}_2\\ 366 | r_w' &\leftarrow r_{w_1} + \rho \cdot r_{w_2} 367 | \end{align*} 368 | \end{enumerate} 369 | \end{tiny} 370 | 371 | \end{frame} 372 | 373 | \section[Wrappup]{Wrappup} 374 | 375 | \begin{frame}{Mysteries \& unsolved things} 376 | \begin{itemize} 377 | \item how HyperNova compares to Protostar 378 | \item prover knows the full witness [TODO update/rm this] 379 | \end{itemize} 380 | 381 | [TODO WIP section] 382 | \end{frame} 383 | 384 | 385 | \begin{frame} 386 | \frametitle{Wrappup} 387 | \begin{itemize} 388 | \item HyperNova: \href{https://eprint.iacr.org/2023/573}{https://eprint.iacr.org/2023/573} 389 | \item multifolding PoC on arkworks: \href{https://github.com/privacy-scaling-explorations/multifolding-poc}{github.com/privacy-scaling-explorations/multifolding-poc} 390 | \item PSE hypernova WIP \href{https://github.com/privacy-scaling-explorations/Nova}{github.com/privacy-scaling-explorations/Nova} 391 | \end{itemize} 392 | 393 | \vspace{2cm} 394 | \tiny{ 395 | $$\text{2023-07-25}$$ 396 | $$\text{\href{https://0xparc.org}{0xPARC}}$$ 397 | } 398 | \end{frame} 399 | 400 | 401 | % from Michael 402 | % - Why Nova? 403 | % - Nova's limitations 404 | % - Why Hypernova 405 | % - Hypernova concepts explained to General Technologist (minimal ZK understanding) 406 | % - Final output 407 | 408 | 409 | 410 | %%%%% 411 | % - We used recursive SNARKs to achieve IVC 412 | % - get a proof and prove that it's verification passes, inside another proof 413 | % - Folding: we're not verifying the entire proof 414 | % - we take n proofs and 'batch' them together 415 | % - at the end of the chain of folds, we just prove that the last fold is correct 416 | % - which implies that all the previous folds were correct 417 | % - Random Linear Combination: combine 2 instances together through a random linear comibnation, and the outputted instance will still satisfy the relation 418 | % - Multifolding SumCheck: SumCheck's polynomial work is trivial, most of the cost comes from Poseidon hash in the transcript 419 | 420 | \end{document} 421 | -------------------------------------------------------------------------------- /notes_spartan.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsfonts} 4 | \usepackage{amsthm} 5 | \usepackage{amsmath} 6 | \usepackage{mathtools} 7 | \usepackage{enumerate} 8 | \usepackage{hyperref} 9 | \usepackage{xcolor} 10 | \usepackage{pgf-umlsd} % diagrams 11 | \usepackage{centernot} 12 | 13 | 14 | % prevent warnings of underfull \hbox: 15 | \usepackage{etoolbox} 16 | \apptocmd{\sloppy}{\hbadness 4000\relax}{}{} 17 | 18 | \theoremstyle{definition} 19 | \newtheorem{definition}{Def}[section] 20 | \newtheorem{theorem}[definition]{Thm} 21 | 22 | % custom lemma environment to set custom numbers 23 | \newtheorem{innerlemma}{Lemma} 24 | \newenvironment{lemma}[1] 25 | {\renewcommand\theinnerlemma{#1}\innerlemma} 26 | {\endinnerlemma} 27 | 28 | 29 | \title{Notes on Spartan} 30 | \author{arnaucube} 31 | \date{April 2023} 32 | 33 | \begin{document} 34 | 35 | \maketitle 36 | 37 | \begin{abstract} 38 | Notes taken while reading about Spartan \cite{cryptoeprint:2019/550}. 39 | 40 | Usually while reading papers I take handwritten notes, this document contains some of them re-written to $LaTeX$. 41 | 42 | The notes are not complete, don't include all the steps neither all the proofs. 43 | \end{abstract} 44 | 45 | \tableofcontents 46 | 47 | \section{R1CS into Sum-Check protocol} 48 | \begin{definition}{R1CS} 49 | $\exists w \in \mathbb{F}^{m - |io| - 1}$ such that $(A \cdot z) \circ (B \cdot z) = (C \cdot z)$, where $z=(io, 1, w)$. 50 | \end{definition} 51 | 52 | 53 | \textbf{Thm 4.1} $\forall$ R1CS instance $x = (\mathbb{F}, A, B, C, io, m, n)$, $\exists$ a degree-3 log m-variate polynomial $G$ such that $\sum_{x \in \{0,1\}^{log m}} G(x) = 0$ iff $\exists$ a witness $w$ such that $Sat_{R1CS}(x, w)=1$. 54 | % \begin{theorem}{4.1} // TODO use theorem gadget 55 | % $\forall$ 56 | % \begin{end} 57 | \vspace{0.5cm} 58 | 59 | % For a RCS instance $x$, let $s = \lceil \log m \rceil$. 60 | 61 | We can view matrices $A, B, C \in \mathbb{F}^{m \times m}$ as functions $\{0,1\}^s \times \{0,1\}^s \rightarrow \mathbb{F}$ ($s= \lceil \log m \rceil$). 62 | For a given witness $w$ to $x$, let $z=(io, 1, w)$. 63 | View $z$ as a function $\{0,1\}^s \rightarrow \mathbb{F}$, so any entry in $z$ can be accessed with a $s$-bit identifier. 64 | 65 | \begin{small} 66 | $$ 67 | F_{io}(x)=\left( \sum_{y \in \{0,1\}^s} A(x, y) \cdot Z(y) \right) \cdot \left( \sum_{y \in \{0,1\}^s} B(x, y) \cdot Z(y) \right) - \sum_{y \in \{0,1\}^s} C(x, y) \cdot Z(y) 68 | $$ 69 | \end{small} 70 | 71 | \begin{lemma}{4.1} 72 | $\forall x \in \{0,1\}^s,~ F_{io}(x)=0$ iff $Sat_{R1CS}(x,w)=1$. 73 | \end{lemma} 74 | 75 | $F_{io}(\cdot)$ is a function, not a polynomial, so it can not be used in the Sum-check protocol. 76 | 77 | $F_{io}(x)$ function is converted to a polynomial by using its polynomial extension $\widetilde{F}_{io}(x): \mathbb{F}^s \rightarrow \mathbb{F}$, 78 | \begin{small} 79 | $$ 80 | \widetilde{F}_{io}(x)=\left( \sum_{y \in \{0,1\}^s} \widetilde{A}(x, y) \cdot \widetilde{Z}(y) \right) \cdot \left( \sum_{y \in \{0,1\}^s} \widetilde{B}(x, y) \cdot \widetilde{Z}(y) \right) - \sum_{y \in \{0,1\}^s} \widetilde{C}(x, y) \cdot \widetilde{Z}(y) 81 | $$ 82 | \end{small} 83 | 84 | \begin{lemma}{4.2} 85 | $\forall x \in \{0,1\}^s,~ \widetilde{F}_{io}(x)=0$ iff $Sat_{R1CS}(x, w)=1$. 86 | \end{lemma} 87 | 88 | (proof: $\forall x \in \{0,1\}^s,~ \widetilde{F}_{io}(x)=F_{io}(x)$, so, result follows from Lemma 4.1.) % TODO link to lemma 89 | 90 | \vspace{0.5cm} 91 | 92 | So, for this, V will need to check that $\widetilde{F}_{io}$ vanishes over the boolean hypercube ($\widetilde{F}_{io}(x)=0 ~\forall x \in \{0,1\}^s$). 93 | 94 | Recall that $\widetilde{F}_{io}(\cdot)$ is a low-degree multivariate polynomial over $\mathbb{F}$ in $s$ variables. 95 | Thus, checking that $\widetilde{F}_{io}$ vanishes over the boolean hypercube is equivalent to checking that $\widetilde{F}_io=0$. 96 | 97 | Thus, V can check $\sum_{x \in \{0,1\}^s} \widetilde{F}_{io}(x)=0$ using the Sum-check protocol (through SZ lemma, V can check if for a random value it equals to 0, and be convinced that applies to all the points whp.). 98 | 99 | But: as $\widetilde{F}_{io}(x)$ is not multilinear, so $\sum_{x\in \{0,1\}^s} \widetilde{F}_{io}(x)=0 \centernot\Longleftrightarrow F_{io}(x)=0 ~\forall x \in \{0,1\}^s$. 100 | Bcs: the $2^s$ terms in the sum might cancel each other even when the individual terms are not zero. 101 | 102 | Solution: combine $\widetilde{F}_{io}(x)$ with $\widetilde{eq}(t, x)$ to get $Q_{io}(t, x)$ which will be the unique multilinear polynomial, and then check that it is a zero-polynomial 103 | 104 | $$Q_{io}(t)= \sum_{x \in \{0,1\}^s} \widetilde{F}_{io}(x) \cdot \widetilde{eq}(t, x)$$ 105 | 106 | where $\widetilde{eq}(t, x) = \prod_{i=1}^s (t_i \cdot x_i + (1- t_i) \cdot (1- x_i))$, which is the MLE of $eq(x,e)= \{ 1 ~\text{if}~ x=e,~ 0 ~\text{otherwise} \}$. 107 | 108 | Basically $Q_{io}(\cdot)$ is a multivariate (the unique multilinear) polynomial such that 109 | $$Q_{io}(t) = \widetilde{F}_{io}(t) ~\forall t \in \{0,1\}^s$$ 110 | thus, $Q_{io}(\cdot)$ is a zero-polynomial iff $\widetilde{F}_{io}(x)=0 ~\forall x\in \{0,1\}^s$. 111 | $\Longleftrightarrow$ iff $\widetilde{F}_{io}(\cdot)$ encodes a witness $w$ such that $Sat_{R1CS}(x, w)=1$. 112 | 113 | $\widetilde{F}_{io}(x)$ has degree 2 in each variable, and $\widetilde{eq}(t, x)$ has degree 1 in each variable, so $Q_{io}(t)$ has degree 3 in each variable. 114 | 115 | To check that $Q_{io}(\cdot)$ is a zero-polynomial: check $Q_{io}(\tau)=0,~ \tau \in^R \mathbb{F}^s$ (Schwartz-Zippel-DeMillo–Lipton lemma) through the sum-check protocol. 116 | 117 | This would mean that the R1CS instance is satisfied. 118 | 119 | 120 | \paragraph{Recap} 121 | \begin{itemize} 122 | \item[] We have that $Sat_{R1CS}(x,w)=1$ iff $F_{io}(x)=0$. 123 | \item[] To be able to use sum-check, we use its polynomial extension $\widetilde{F}_{io}(x)$, using sum-check to prove that $\widetilde{F}_{io}(x) =0 ~\forall x \in \{0, 1\}^s$, which means that $Sat_{R1CS}(x,~w)=1$. 124 | \item[] To prevent potential canceling terms, we combine $\widetilde{F}_{io}(x)$ with $\widetilde{eq}(t, x)$, obtaining $G_{io, \tau}(x)= \widetilde{F}_{io}(x) \cdot \widetilde{eq}(t, x)$. 125 | \item[] Thus $Q_{io}(t)= \sum_{x \in \{0,1\}^s} \widetilde{F}_{io}(x) \cdot \widetilde{eq}(t, x)$, and then we prove that $Q_{io}(\tau)=0$, for $\tau \in^R \mathbb{F}^s$. 126 | \end{itemize} 127 | 128 | \section{NIZKs with succinct proofs for R1CS} 129 | 130 | From Thm 4.1: to check R1CS instance $(\mathbb{F}, A, B, C, io, m, n)$ V can check if 131 | $\sum_{x \in \{0,1\}^s} G_{io, \tau} (x) = 0$, which through sum-check protocol can be reduced to $e_x = G_{io, \tau} (r_x)$, where $r_x \in \mathbb{F}^s$. 132 | 133 | Recall: $G_{io, \tau}(x) = \widetilde{F}_{io}(x) \cdot \widetilde{eq}(\tau, x)$. 134 | 135 | Evaluating $\widetilde{eq}(\tau, r_x)$ takes $O(log~m)$, but to evaluate $\widetilde{F}_{io}(r_x)$, V needs to evaluate 136 | $$\widetilde{A}(r_x, y), \widetilde{B}(r_x, y), \widetilde{C}(r_x, y), \widetilde{Z}(y),~ \forall y \in \{0,1\}^s$$ 137 | 138 | which requires 3 sum-check instances (\begin{scriptsize} 139 | $\left( \sum_{y \in \{0,1\}^s} \widetilde{A}(x, y) \cdot \widetilde{Z}(y) \right)$,\\ $\left( \sum_{y \in \{0,1\}^s} \widetilde{B}(x, y) \cdot \widetilde{Z}(y) \right)$, $\left( \sum_{y \in \{0,1\}^s} \widetilde{C}(x, y) \cdot \widetilde{Z}(y) \right)$ 140 | \end{scriptsize}), one for each summation in\\ $\widetilde{F}_{io}(x)$. 141 | 142 | But note that evaluations of $\widetilde{Z}(y) ~\forall y \in \{0,1\}^s$ are already known as $(io, 1, w)$. 143 | 144 | Solution: combination of 3 protocols: 145 | \begin{itemize} 146 | \item Sum-check protocol 147 | \item randomized mini protocol 148 | \item polynomial commitment scheme 149 | \end{itemize} 150 | Basically to do a random linear combination of the 3 summations to end up doing just a single sum-check. 151 | 152 | Observation: let $\widetilde{F}_{io}(r_x) = \overline{A}(r_x) \cdot \overline{B}(r_x) - \overline{C}(r_x)$, where 153 | $$\overline{A}(r_x) = \sum_{y \in \{0,1\}} \widetilde{A}(r_x, y) \cdot \widetilde{Z}(y),~~\overline{B}(r_x) = \sum_{y \in \{0,1\}} \widetilde{B}(r_x, y) \cdot \widetilde{Z}(y)$$ 154 | $$\overline{C}(r_x) = \sum_{y \in \{0,1\}} \widetilde{C}(r_x, y) \cdot \widetilde{Z}(y)$$ 155 | 156 | Prover makes 3 separate claims: $\overline{A}(r_x)=v_A,~ \overline{B}(r_x)=v_B,~ \overline{C}(r_x)=v_C$, 157 | then V evaluates: 158 | $$G_{io, \tau}(r_x) = (v_A \cdot v_B - v_C) \cdot \widetilde{eq}(r_x, \tau)$$ 159 | 160 | 161 | \begin{footnotesize} 162 | which equals to 163 | $$=\left(\overline{A}(r_x) \cdot \overline{B}(r_x) - \overline{C}(r_x)\right) \cdot \widetilde{eq}(r_x, \tau)=$$ 164 | $$\left(\left(\sum_{y \in \{0,1\}} \widetilde{A}(r_x, y) \cdot \widetilde{Z}(y)\right) \cdot \left(\sum_{y \in \{0,1\}} \widetilde{B}(r_x, y) \cdot \widetilde{Z}(y)\right) - \sum_{y \in \{0,1\}} \widetilde{C}(r_x, y) \cdot \widetilde{Z}(y)\right) \cdot \widetilde{eq}(r_x, \tau)$$ 165 | \end{footnotesize} 166 | 167 | \vspace{0.5cm} 168 | 169 | This would be 3 sum-check protocol instances (3 claims: $\overline{A}(r_x)=v_A$, $\overline{B}(r_x)=v_B$, $\overline{C}(r_x)=v_C$). 170 | 171 | Instead, combine 3 claims into a single claim: 172 | 173 | \begin{itemize} 174 | \item V samples $r_A, r_B, r_C \in^R \mathbb{F}$, and computes $c= r_A v_A + r_B v_B + r_C v_C$. 175 | \item V, P use sum-check protocol to check: 176 | $$r_A \cdot \overline{A}(r_x) + r_B \cdot \overline{B}(r_x) + r_C \cdot \overline{C}(r_x) == c$$ 177 | 178 | 179 | % Let $L(r_x) = r_A \cdot \overline{A}(r_x) +r_B \cdot \overline{B}(r_x) +r_C \cdot \overline{C}(r_x)$, 180 | Let 181 | \begin{small} 182 | \begin{align*} 183 | &L(r_x) = r_A \cdot \overline{A}(r_x) +r_B \cdot \overline{B}(r_x) +r_C \cdot \overline{C}(r_x)\\ 184 | &= \sum_{y \in \{0,1\}^s} 185 | \left( r_A \cdot \widetilde{A}(r_x, y) \cdot \widetilde{Z}(y) 186 | + r_B \cdot \widetilde{B}(r_x, y) \cdot \widetilde{Z}(y) 187 | + r_C \cdot \widetilde{C}(r_x, y) \cdot \widetilde{Z}(y) \right)\\ 188 | &= \sum_{y \in \{0,1\}^s} M_{r_x}(y) 189 | \end{align*} 190 | \end{small} 191 | 192 | $M_{r_x}(y)$ is a s-variate polynomial with deg $\leq 2$ in each variable ($\Longleftrightarrow \mu = s,~ l=2,~ T=c$). 193 | 194 | \end{itemize} 195 | 196 | 197 | \begin{align*} 198 | M_{r_x}(r_y) &= 199 | r_A \cdot \widetilde{A}(r_x, r_y) \cdot \widetilde{Z}(r_y) 200 | + r_B \cdot \widetilde{B}(r_x, r_y) \cdot \widetilde{Z}(r_y) 201 | + r_C \cdot \widetilde{C}(r_x, r_y) \cdot \widetilde{Z}(r_y)\\ 202 | &= 203 | (r_A \cdot \widetilde{A}(r_x, r_y) 204 | + r_B \cdot \widetilde{B}(r_x, r_y) 205 | + r_C \cdot \widetilde{C}(r_x, r_y)) \cdot \widetilde{Z}(r_y)\\ 206 | \end{align*} 207 | 208 | only one term in $M_{r_x}(r_y)$ depends on prover's witness: $\widetilde{Z}(r_y)$, the other terms can be computed locally by V in $O(n)$ time (Section 6 of the paper for sub-linear in $n$). 209 | 210 | Instead of evaluating $\widetilde{Z}(r_y)$ in $O(|w|)$ communications, P sends a commitment to $\widetilde{w}(\cdot)$ (= MLE of the witness $w$) to V before the first instance of the sum-check protocol. 211 | 212 | 213 | \paragraph{Recap} 214 | \begin{itemize} 215 | \item[] To check the R1CS instance, V can check $\sum_{x \in \{0,1\}^s} G_{io, \tau} (x) = 0$, which through the sum-check is reduced to $e_x = G_{io, \tau} (r_x)$, for $r_x \in \mathbb{F}^s$. 216 | \item[] Evaluating $G_{io, \tau}(x)$ ($G_{io, \tau}(x) = \widetilde{F}_{io}(x) \cdot \widetilde{eq}(\tau, x)$) is not cheap. Evaluating $\widetilde{eq}(\tau, r_x)$ takes $O(log~m)$, but to evaluate $\widetilde{F}_{io}(r_x)$, V needs to evaluate $\widetilde{A}, \widetilde{B}, \widetilde{C}, \widetilde{Z},~ \forall y \in \{0,1\}^s$ 217 | % \item[] Solution: combine 3 protocols: sum-check protocol, randomized mini protocol, polynomial commitment scheme. 218 | \item[] P makes 3 separate claims: $\overline{A}(r_x)=v_A,~ \overline{B}(r_x)=v_B,~ \overline{C}(r_x)=v_C$, so V can evaluate $G_{io, \tau}(r_x) = (v_A \cdot v_B - v_C) \cdot \widetilde{eq}(r_x, \tau)$ 219 | \item[] The previous claims are combined into a single claim (random linear combination) to use only a single sum-check protocol: 220 | \begin{itemize} 221 | \item[] P: $c= r_A v_A + r_B v_B + r_C v_C$, for $r_A, r_B, r_C \in^R \mathbb{F}$ 222 | \item[] V, P: sum-check $r_A \cdot \overline{A}(r_x) + r_B \cdot \overline{B}(r_x) + r_C \cdot \overline{C}(r_x) == c$ 223 | \end{itemize} 224 | \item[] $c=L(r_x)=\sum_{y \in \{0,1\}^s} M_{r_x}(y)$, where $M_{r_x}(y)$ is a s-variate polynomial with deg $\leq 2$ in each variable ($\Longleftrightarrow \mu = s,~ l=2,~ T=c$). Only $\widetilde{Z}(r_y)$ depends on P's witness, the other terms can be computed locally by V. 225 | \item[] Instead of evaluating $\widetilde{Z}(r_y)$ in $O(|w|)$ communications, P uses a commitment to $\widetilde{w}(\cdot)$ (= MLE of the witness $w$). 226 | \end{itemize} 227 | 228 | \subsection{Full protocol} 229 | \begin{footnotesize} 230 | (Recall: Sum-Check params: $\mu$: n vars, n rounds, $l$: degree in each variable upper bound, $T$: claimed result.) 231 | \end{footnotesize} 232 | 233 | \begin{itemize} 234 | \item $pp \leftarrow Setup(1^{\lambda})$: invoke $pp \leftarrow PC.Setup(1^{\lambda}, log m)$; output $pp$ 235 | \item $b \leftarrow (\mathbb{F}, A,B,C, io, m, n)$: 236 | \begin{enumerate} 237 | \item P: $(C, S) \leftarrow PC.Commit(pp, \widetilde{w})$ and send $C$ to V 238 | \item V: send $\tau \in^R \mathbb{F}^{log~m}$ to P 239 | \item let $T_1=0,~ \mu_1=log~m,~ l_1=3$ 240 | \item V: set $r_x \in^R \mathbb{F}^{\mu_1}$ 241 | \item Sum-check 1. $e_x \leftarrow (\mu_1, l_1, T_1)$ 242 | \item P: compute $v_A=\overline{A}(r_x),~ v_B=\overline{B}(r_x),~ v_C=\overline{C}(r_x)$, send $(v_A, v_B, v_C)$ to V 243 | \item V: abort with $b=0$ if $e_x \neq (v_A \cdot v_B - v_C)\cdot \widetilde{eq}(r_x, \tau)$ 244 | \item V: send $r_A, r_B, r_C \in^R \mathbb{F}$ to P 245 | \item let $T_2 = r_A \cdot v_A + r_B \cdot v_B + r_C \cdot v_C,~ \mu_2=log~m,~ l_2=2$ 246 | \item V: set $r_y \in^R \mathbb{F}^{\mu_2}$ 247 | \item Sum-check 2. $e_y \leftarrow (\mu_2, l_2, T_2)$ 248 | \item P: $v \leftarrow \widetilde{w}(r_y[1..])$, send $v$ to V 249 | \item $b_e \leftarrow (pp, C, r_y, v, \mu_2)$ 250 | \item V: abort with $b=0$ if $b_e==0$ 251 | \item V: $v_z \leftarrow (1 - r_y[0]) \cdot \widetilde{w}(r_y [1..]) + r_y[0] \widetilde{(io, 1)} (r_y[1..])$ 252 | \item V: $v_1 \leftarrow \widetilde{A}(r_x, r_y),~ v_2 \leftarrow \widetilde{B}(r_x, r_y),~ v_3 \leftarrow \widetilde{C}(r_x, r_y)$ 253 | \item V: abort with $b=0$ if $e_y \neq (r_A v_1 + r_B v_2 + r_C v_3) \cdot v_z$ 254 | \item V: output $b=1$ 255 | \end{enumerate} 256 | \end{itemize} 257 | 258 | Section 6 of the paper, describes how in step 16, instead of evaluating $\widetilde{A},~\widetilde{B},~\widetilde{C}$ at $r_x,~r_y$ with $O(n)$ costs, P commits to $\widetilde{A},~\widetilde{B},~\widetilde{C}$ and later provides proofs of openings. 259 | 260 | In a practical implementation those commits to $\widetilde{A},~\widetilde{B},~\widetilde{C}$ could be done in a preprocessing step. 261 | 262 | \vspace{1cm} 263 | \framebox{WIP: covered until sec.6} 264 | 265 | 266 | 267 | \bibliography{paper-notes.bib} 268 | \bibliographystyle{unsrt} 269 | 270 | \end{document} 271 | -------------------------------------------------------------------------------- /ipa.sage: -------------------------------------------------------------------------------- 1 | # This file contains two Inner Product Argument implementations: 2 | # - Bulletproofs version: https://eprint.iacr.org/2017/1066.pdf 3 | # - Halo version: https://eprint.iacr.org/2019/1021.pdf 4 | 5 | 6 | 7 | # IPA_bulletproofs implements the IPA version from the Bulletproofs paper: https://eprint.iacr.org/2017/1066.pdf 8 | # https://doc-internal.dalek.rs/bulletproofs/notes/inner_product_proof/index.html 9 | class IPA_bulletproofs: 10 | def __init__(self, F, E, g, d): 11 | self.g = g 12 | self.F = F 13 | self.E = E 14 | self.d = d 15 | # TODO: 16 | # Setup: 17 | self.h = E.random_element() # TMP 18 | self.gs = random_values(E, d) 19 | self.hs = random_values(E, d) 20 | 21 | # a: aᵢ ∈ 𝔽 coefficients of p(X) 22 | # r: blinding factor 23 | def commit(self, a, b): 24 | P = inner_product_point(a, self.gs) + inner_product_point(b, self.hs) 25 | return P 26 | 27 | def evaluate(self, a, x_powers): 28 | return inner_product_field(a, x_powers) 29 | 30 | def ipa(self, a_, b_, u, U): 31 | G = self.gs 32 | H = self.hs 33 | a = a_ 34 | b = b_ 35 | 36 | k = int(math.log(self.d, 2)) 37 | L = [None] * k 38 | R = [None] * k 39 | 40 | for j in reversed(range(0, k)): 41 | m = len(a)/2 42 | a_lo = a[:m] 43 | a_hi = a[m:] 44 | b_lo = b[:m] 45 | b_hi = b[m:] 46 | H_lo = H[:m] 47 | H_hi = H[m:] 48 | G_lo = G[:m] 49 | G_hi = G[m:] 50 | 51 | # Lⱼ = + [lⱼ] H + [] U 52 | L[j] = inner_product_point(a_lo, G_hi) + inner_product_point(b_hi, H_lo) + int(inner_product_field(a_lo, b_hi)) * U 53 | # Rⱼ = + [rⱼ] H + [] U 54 | R[j] = inner_product_point(a_hi, G_lo) + inner_product_point(b_lo, H_hi) + int(inner_product_field(a_hi, b_lo)) * U 55 | 56 | # use the random challenge uⱼ ∈ 𝕀 generated by the verifier 57 | u_ = u[j] # uⱼ 58 | u_inv = u[j]^(-1) # uⱼ⁻¹ 59 | 60 | a = vec_add(vec_scalar_mul_field(a_lo, u_), vec_scalar_mul_field(a_hi, u_inv)) 61 | b = vec_add(vec_scalar_mul_field(b_lo, u_inv), vec_scalar_mul_field(b_hi, u_)) 62 | G = vec_add(vec_scalar_mul_point(G_lo, u_inv), vec_scalar_mul_point(G_hi, u_)) 63 | H = vec_add(vec_scalar_mul_point(H_lo, u_), vec_scalar_mul_point(H_hi, u_inv)) 64 | 65 | assert len(a)==1 66 | assert len(b)==1 67 | assert len(G)==1 68 | assert len(H)==1 69 | # a, b, G have length=1 70 | # L, R are the "cross-terms" of the inner product 71 | return a[0], b[0], G[0], H[0], L, R 72 | 73 | def verify(self, P, a, v, x_powers, u, U, L, R, b_ipa, G_ipa, H_ipa): 74 | b = b_ipa 75 | G = G_ipa 76 | H = H_ipa 77 | 78 | # Q_0 = P' ⋅ ∑ ( [uⱼ²] Lⱼ + [uⱼ⁻²] Rⱼ) 79 | C = P 80 | for j in range(len(L)): 81 | u_ = u[j] # uⱼ 82 | u_inv = u[j]^(-1) # uⱼ⁻² 83 | 84 | # ∑ ( [uⱼ²] Lⱼ + [uⱼ⁻²] Rⱼ) 85 | C = C + int(u_^2) * L[j] + int(u_inv^2) * R[j] 86 | 87 | D = int(a) * G + int(b) * H + int(a * b)*U 88 | 89 | return C == D 90 | 91 | # IPA_halo implements the modified IPA from the Halo paper: https://eprint.iacr.org/2019/1021.pdf 92 | class IPA_halo: 93 | def __init__(self, F, E, g, d): 94 | self.g = g 95 | self.F = F 96 | self.E = E 97 | self.d = d 98 | 99 | self.h = E.random_element() # TMP 100 | self.gs = random_values(E, d) 101 | self.hs = random_values(E, d) 102 | # print(" h=", self.h) 103 | # print(" G=", self.gs) 104 | # print(" H=", self.hs) 105 | 106 | def commit(self, a, r): 107 | P = inner_product_point(a, self.gs) + r * self.h 108 | return P 109 | 110 | def evaluate(self, a, x_powers): 111 | return inner_product_field(a, x_powers) 112 | 113 | def ipa(self, a_, x_powers, u, U): # prove 114 | print(" method ipa():") 115 | G = self.gs 116 | a = a_ 117 | b = x_powers 118 | 119 | k = int(math.log(self.d, 2)) 120 | l = [None] * k 121 | r = [None] * k 122 | L = [None] * k 123 | R = [None] * k 124 | 125 | for j in reversed(range(0, k)): 126 | print(" j =", j) 127 | print(" len(a) = n =", len(a)) 128 | print(" m = n/2 =", len(a)/2) 129 | m = len(a)/2 130 | a_lo = a[:m] 131 | a_hi = a[m:] 132 | b_lo = b[:m] 133 | b_hi = b[m:] 134 | G_lo = G[:m] 135 | G_hi = G[m:] 136 | 137 | print(" Split into a_lo,hi b_lo,hi, G_lo,hi:") 138 | print(" a", a) 139 | print(" a_lo", a_lo) 140 | print(" a_hi", a_hi) 141 | print(" b", b) 142 | print(" b_lo", b_lo) 143 | print(" b_hi", b_hi) 144 | print(" G", G) 145 | print(" G_lo", G_lo) 146 | print(" G_hi", G_hi) 147 | 148 | l[j] = self.F.random_element() # random blinding factor 149 | r[j] = self.F.random_element() # random blinding factor 150 | print(" random blinding factors:") 151 | print(" l[j]", l[j]) 152 | print(" r[j]", r[j]) 153 | 154 | # Lⱼ = + [lⱼ] H + [] U 155 | L[j] = inner_product_point(a_lo, G_hi) + int(l[j]) * self.h + int(inner_product_field(a_lo, b_hi)) * U 156 | # Rⱼ = + [rⱼ] H + [] U 157 | R[j] = inner_product_point(a_hi, G_lo) + int(r[j]) * self.h + int(inner_product_field(a_hi, b_lo)) * U 158 | 159 | print(" Compute Lⱼ = + [lⱼ] H + [] U") 160 | print(" L[j]", L[j]) 161 | print(" Compute Rⱼ = + [rⱼ] H + [] U") 162 | print(" R[j]", R[j]) 163 | 164 | # use the random challenge uⱼ ∈ 𝕀 generated by the verifier 165 | u_ = u[j] # uⱼ 166 | u_inv = self.F(u[j])^(-1) # uⱼ⁻¹ 167 | print(" u_j", u_) 168 | print(" u_j^-1", u_inv) 169 | 170 | a = vec_add(vec_scalar_mul_field(a_lo, u_), vec_scalar_mul_field(a_hi, u_inv)) 171 | b = vec_add(vec_scalar_mul_field(b_lo, u_inv), vec_scalar_mul_field(b_hi, u_)) 172 | G = vec_add(vec_scalar_mul_point(G_lo, u_inv), vec_scalar_mul_point(G_hi, u_)) 173 | print(" new a, b, G") 174 | print(" a =", a) 175 | print(" b =", b) 176 | print(" G =", G) 177 | 178 | assert len(a)==1 179 | assert len(b)==1 180 | assert len(G)==1 181 | # a, b, G have length=1 182 | # l, r are random blinding factors 183 | # L, R are the "cross-terms" of the inner product 184 | return a[0], l, r, L, R 185 | 186 | def verify(self, P, a, v, x_powers, r, u, U, lj, rj, L, R): 187 | print("method verify()") 188 | 189 | # compute P' = P + [v] U 190 | P = P + int(v) * U 191 | 192 | s = build_s_from_us(u, self.d) 193 | b = inner_product_field(s, x_powers) 194 | G = inner_product_point(s, self.gs) 195 | 196 | # synthetic blinding factor 197 | # r' = r + ∑ ( lⱼ uⱼ² + rⱼ uⱼ⁻²) 198 | print(" synthetic blinding factor r' = r + ∑ ( lⱼ uⱼ² + rⱼ uⱼ⁻²)") 199 | r_ = r 200 | print(" r_ =", r_) 201 | # Q_0 = P' ⋅ ∑ ( [uⱼ²] Lⱼ + [uⱼ⁻²] Rⱼ) 202 | print(" Q_0 = P' ⋅ ∑ ( [uⱼ²] Lⱼ + [uⱼ⁻²] Rⱼ)") 203 | Q_0 = P 204 | print(" Q_0 =", Q_0) 205 | for j in range(len(u)): 206 | print(" j =", j) 207 | u_ = u[j] # uⱼ 208 | u_inv = u[j]^(-1) # uⱼ⁻² 209 | 210 | # ∑ ( [uⱼ²] Lⱼ + [uⱼ⁻²] Rⱼ) 211 | Q_0 = Q_0 + int(u[j]^2) * L[j] + int(u_inv^2) * R[j] 212 | print(" Q_0 =", Q_0) 213 | 214 | r_ = r_ + lj[j] * (u_^2) + rj[j] * (u_inv^2) 215 | print(" r_ =", r_) 216 | 217 | Q_1 = int(a) * G + int(r_) * self.h + int(a * b)*U 218 | print(" Q_1", Q_1) 219 | # Q_1_ = int(a) * (G + int(b)*U) + int(r_) * self.h 220 | 221 | return Q_0 == Q_1 222 | 223 | 224 | # s = ( 225 | # u₁⁻¹ u₂⁻¹ … uₖ⁻¹, 226 | # u₁ u₂⁻¹ … uₖ⁻¹, 227 | # u₁⁻¹ u₂ … uₖ⁻¹, 228 | # u₁ u₂ … uₖ⁻¹, 229 | # ⋮ ⋮ ⋮ 230 | # u₁ u₂ … uₖ 231 | # ) 232 | def build_s_from_us(u, d): 233 | k = int(math.log(d, 2)) 234 | s = [1]*d 235 | t = d 236 | for j in reversed(range(k)): 237 | t = t/2 238 | c = 0 239 | for i in range(d): 240 | if c=t*2: 246 | c=0 247 | 248 | return s 249 | 250 | 251 | 252 | 253 | def powers_of(g, d): 254 | r = [None] * d 255 | for i in range(d): 256 | r[i] = g^i 257 | return r 258 | 259 | 260 | def multiples_of(g, d): 261 | r = [None] * d 262 | for i in range(d): 263 | r[i] = g*i 264 | return r 265 | 266 | def random_values(G, d): 267 | r = [None] * d 268 | for i in range(d): 269 | r[i] = G.random_element() 270 | return r 271 | 272 | def inner_product_field(a, b): 273 | assert len(a) == len(b) 274 | c = 0 275 | for i in range(len(a)): 276 | c = c + a[i] * b[i] 277 | 278 | return c 279 | 280 | def inner_product_point(a, b): 281 | assert len(a) == len(b) 282 | c = 0 283 | for i in range(len(a)): 284 | c = c + int(a[i]) * b[i] 285 | 286 | return c 287 | 288 | def vec_add(a, b): 289 | assert len(a) == len(b) 290 | return [x + y for x, y in zip(a, b)] 291 | 292 | def vec_mul(a, b): 293 | assert len(a) == len(b) 294 | return [x * y for x, y in zip(a, b)] 295 | 296 | def vec_scalar_mul_field(a, n): 297 | r = [None]*len(a) 298 | for i in range(len(a)): 299 | r[i] = a[i]*n 300 | return r 301 | 302 | def vec_scalar_mul_point(a, n): 303 | r = [None]*len(a) 304 | for i in range(len(a)): 305 | r[i] = a[i]*int(n) 306 | return r 307 | 308 | 309 | # Tests 310 | import unittest, operator 311 | 312 | # Ethereum elliptic curve 313 | p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F 314 | a = 0 315 | b = 7 316 | Fp = GF(p) 317 | E = EllipticCurve(Fp, [a,b]) 318 | GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 319 | GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 320 | g = E(GX,GY) 321 | n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 322 | h = 1 323 | q = g.order() 324 | Fq = GF(q) 325 | 326 | # simpler curve values 327 | # p = 19 328 | # Fp = GF(p) 329 | # E = EllipticCurve(Fp,[0,3]) 330 | # g = E(1, 2) 331 | # q = g.order() 332 | # Fq = GF(q) 333 | 334 | print(E) 335 | print(Fq) 336 | assert is_prime(p) 337 | assert is_prime(q) 338 | assert g * q == 0 339 | 340 | class TestUtils(unittest.TestCase): 341 | def test_vecs(self): 342 | a = [1, 2, 3, 4, 5] 343 | b = [1, 2, 3, 4, 5] 344 | 345 | c = vec_scalar_mul_field(a, 10) 346 | assert c == [10, 20, 30, 40, 50] 347 | 348 | c = inner_product_field(a, b) 349 | assert c == 55 350 | 351 | # check that with b = (1, x, x^2, ..., x^{d-1}) is the same 352 | # than evaluating p(x) with coefficients a_i, at x 353 | a = [Fq(1), Fq(2), Fq(3), Fq(4), Fq(5), Fq(6), Fq(7), Fq(8)] 354 | z = Fq(3) 355 | b = powers_of(z, 8) 356 | c = inner_product_field(a, b) 357 | 358 | x = PolynomialRing(Fq, 'x').gen() 359 | px = 1 + 2*x + 3*x^2 + 4*x^3 + 5*x^4 + 6*x^5 + 7*x^6 + 8*x^7 360 | assert c == px(x=z) 361 | 362 | 363 | class TestIPA_bulletproofs(unittest.TestCase): 364 | def test_inner_product_argument(self): 365 | d = 8 366 | ipa = IPA_bulletproofs(Fq, E, g, d) 367 | 368 | # prover 369 | # p(x) = 1 + 2x + 3x² + 4x³ + 5x⁴ + 6x⁵ + 7x⁶ + 8x⁷ 370 | a = [ipa.F(1), ipa.F(2), ipa.F(3), ipa.F(4), ipa.F(5), ipa.F(6), ipa.F(7), ipa.F(8)] 371 | x = ipa.F(3) 372 | b = powers_of(x, ipa.d) # = b 373 | 374 | # prover 375 | P = ipa.commit(a, b) 376 | print("commit", P) 377 | v = ipa.evaluate(a, b) 378 | print("v", v) 379 | 380 | # verifier generate random challenges {uᵢ} ∈ 𝕀 and U ∈ 𝔾 381 | U = ipa.E.random_element() 382 | k = int(math.log(d, 2)) 383 | u = [None] * k 384 | for j in reversed(range(0, k)): 385 | u[j] = ipa.F.random_element() 386 | while (u[j] == 0): # prevent u[j] from being 0 387 | u[j] = ipa.F.random_element() 388 | 389 | P = P + int(inner_product_field(a, b)) * U 390 | 391 | # prover 392 | a_ipa, b_ipa, G_ipa, H_ipa, L, R = ipa.ipa(a, b, u, U) 393 | 394 | # verifier 395 | print("P", P) 396 | print("a_ipa", a_ipa) 397 | verif = ipa.verify(P, a_ipa, v, b, u, U, L, R, b_ipa, G_ipa, H_ipa) 398 | print("Verification:", verif) 399 | assert verif == True 400 | 401 | 402 | class TestIPA_halo(unittest.TestCase): 403 | def test_homomorphic_property(self): 404 | ipa = IPA_halo(Fq, E, g, 5) 405 | 406 | a = [1, 2, 3, 4, 5] 407 | b = [1, 2, 3, 4, 5] 408 | c = vec_add(a, b) 409 | assert c == [2,4,6,8,10] 410 | 411 | r = int(ipa.F.random_element()) 412 | s = int(ipa.F.random_element()) 413 | vc_a = ipa.commit(a, r) 414 | vc_b = ipa.commit(b, s) 415 | 416 | # com(a, r) + com(b, s) == com(a+b, r+s) 417 | expected_vc_c = ipa.commit(vec_add(a, b), r+s) 418 | vc_c = vc_a + vc_b 419 | assert vc_c == expected_vc_c 420 | 421 | def test_inner_product_argument(self): 422 | d = 8 423 | ipa = IPA_halo(Fq, E, g, d) 424 | 425 | # prover 426 | # p(x) = 1 + 2x + 3x² + 4x³ + 5x⁴ + 6x⁵ + 7x⁶ + 8x⁷ 427 | a = [ipa.F(1), ipa.F(2), ipa.F(3), ipa.F(4), ipa.F(5), ipa.F(6), ipa.F(7), ipa.F(8)] 428 | x = ipa.F(3) 429 | x_powers = powers_of(x, ipa.d) # = b 430 | 431 | # blinding factor 432 | r = int(ipa.F.random_element()) 433 | 434 | # prover 435 | P = ipa.commit(a, r) 436 | print("commit", P) 437 | v = ipa.evaluate(a, x_powers) 438 | print("v", v) 439 | 440 | # verifier generate random challenges {uᵢ} ∈ 𝕀 and U ∈ 𝔾 441 | # This might be obtained from the hash of the transcript 442 | # (Fiat-Shamir heuristic for non-interactive version) 443 | U = ipa.E.random_element() 444 | k = int(math.log(ipa.d, 2)) 445 | u = [None] * k 446 | for j in reversed(range(0, k)): 447 | u[j] = ipa.F.random_element() 448 | while (u[j] == 0): # prevent u[j] from being 0 449 | u[j] = ipa.F.random_element() 450 | 451 | # prover 452 | a_ipa, lj, rj, L, R = ipa.ipa(a, x_powers, u, U) 453 | 454 | # verifier 455 | print("P", P) 456 | print("a_ipa", a_ipa) 457 | print("\n Verify:") 458 | verif = ipa.verify(P, a_ipa, v, x_powers, r, u, U, lj, rj, L, R) 459 | assert verif == True 460 | 461 | 462 | if __name__ == '__main__': 463 | unittest.main() 464 | --------------------------------------------------------------------------------