diff --git a/prover-ray/wiop/compilers/global/global.go b/prover-ray/wiop/compilers/global/global.go index 191188d1a56..3ad46fec73e 100644 --- a/prover-ray/wiop/compilers/global/global.go +++ b/prover-ray/wiop/compilers/global/global.go @@ -508,13 +508,11 @@ func (gv *Verifier) Check(rt wiop.Runtime) error { // --- Recombine quotient shares: Q(r) = Σ_k r^{kn} · Q_k(r) --- qr := field.ElemZero() rPowKN := field.ElemOne() // r^{kn}, starts at r^0 = 1 - for k, claim := range bkt.quotientClaims { - _ = k + for _, claim := range bkt.quotientClaims { qk := rt.GetCellValue(claim) // Q_k(r) qr = qr.Add(rPowKN.Mul(qk)) // Advance: r^{(k+1)n} = r^{kn} · r^n - rPowN := computeAnnihilator(r, n) // r^n − 1 + 1 = r^n ... just compute r^n - rPowN = rPowN.Add(field.ElemOne()) + rPowN := expFieldElem(r, n) rPowKN = rPowKN.Mul(rPowN) } diff --git a/prover-ray/wiop/compilers/global/global_test.go b/prover-ray/wiop/compilers/global/global_test.go index 0e3071a23e1..f59e4b68db9 100644 --- a/prover-ray/wiop/compilers/global/global_test.go +++ b/prover-ray/wiop/compilers/global/global_test.go @@ -1,6 +1,11 @@ package global_test import ( + "os" + "os/exec" + "path/filepath" + goruntime "runtime" + "strings" "testing" "github.com/consensys/linea-monorepo/prover-ray/wiop" @@ -41,3 +46,71 @@ func TestCompile_Soundness(t *testing.T) { }) } } + +func Test_zigTest(t *testing.T) { + zigTest(t) +} + +func zigTest(t *testing.T) { + t.Helper() + zigPath, zigErr := exec.LookPath("zig") + + repoRoot := repoRootFromTest(t) + outputDir := filepath.Join(repoRoot, "wiop", "zigverifiers") + zigStatics := filepath.Join(repoRoot, "wiop", "zigstatics", "koalabear_field.zig") + require.NoError(t, os.MkdirAll(outputDir, 0o755)) + + for _, build := range wioptest.VanishingScenarios() { + sc := build() + t.Run(sc.Name, func(t *testing.T) { + global.Compile(sc.Sys) + + rt := wiop.NewRuntime(sc.Sys) + sc.AssignHonest(&rt) + require.NoError(t, wioptest.RunAndVerify(&rt), + "Go verifier must accept before generating the equivalent Zig verifier") + + src, err := findGlobalVerifier(t, sc.Sys).GenerateZig(rt) + require.NoError(t, err) + + zigFile := filepath.Join(outputDir, zigVerifierFileName(sc.Name)) + require.NoError(t, os.WriteFile(zigFile, src, 0o600)) + if zigErr != nil { + return + } + + //nolint:gosec // Test executes the local Zig compiler on generated verifier source. + cmd := exec.Command(zigPath, "test", "--dep", "koalabear", "-Mroot="+zigFile, "-Mkoalabear="+zigStatics) + cmd.Dir = repoRoot + out, err := cmd.CombinedOutput() + require.NoError(t, err, "zig test failed:\n%s", out) + }) + } + if zigErr != nil { + t.Skip("zig binary is not installed; generated Zig files were written but not compiled") + } +} + +func findGlobalVerifier(t *testing.T, sys *wiop.System) *global.Verifier { + t.Helper() + for _, r := range sys.Rounds { + for _, action := range r.VerifierActions { + if verifier, ok := action.(*global.Verifier); ok { + return verifier + } + } + } + require.FailNow(t, "compiled system has no global verifier action") + return nil +} + +func repoRootFromTest(t *testing.T) string { + t.Helper() + _, file, _, ok := goruntime.Caller(0) + require.True(t, ok, "runtime.Caller should locate the test file") + return filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", "..")) +} + +func zigVerifierFileName(scenarioName string) string { + return "global_verifier_" + strings.ToLower(scenarioName) + ".zig" +} diff --git a/prover-ray/wiop/compilers/global/zig.go b/prover-ray/wiop/compilers/global/zig.go new file mode 100644 index 00000000000..76f7eb7440e --- /dev/null +++ b/prover-ray/wiop/compilers/global/zig.go @@ -0,0 +1,298 @@ +package global + +import ( + "fmt" + "strings" + + "github.com/consensys/linea-monorepo/prover-ray/maths/koalabear/field" + "github.com/consensys/linea-monorepo/prover-ray/wiop" +) + +// GenerateZig returns standalone Zig source that mirrors [Verifier.Check] for +// this verifier and runtime snapshot. +// +// The generated program embeds only verifier-visible scalar data from rt: +// random coins, witness evaluation claim cells, and quotient claim cells. It +// deliberately does not mirror wiop.Runtime in Zig. +func (gv *Verifier) GenerateZig(rt wiop.Runtime) ([]byte, error) { + if gv == nil { + return nil, fmt.Errorf("wiop/compilers: cannot generate Zig for a nil global verifier") + } + gen := newZigVerifierGenerator(gv, rt) + return gen.generate() +} + +type zigVerifierGenerator struct { + gv *Verifier + rt wiop.Runtime + witnessNames map[colViewKey]string + out zigWriter +} + +func newZigVerifierGenerator(gv *Verifier, rt wiop.Runtime) *zigVerifierGenerator { + witnessNames := make(map[colViewKey]string, len(gv.witnessViews)) + for i, cv := range gv.witnessViews { + key := colViewKey{id: cv.Column.Context.ID, shift: cv.ShiftingOffset} + witnessNames[key] = fmt.Sprintf("witness_eval_%d", i) + } + return &zigVerifierGenerator{ + gv: gv, + rt: rt, + witnessNames: witnessNames, + } +} + +func (g *zigVerifierGenerator) generate() ([]byte, error) { + w := &g.out + w.Line("// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT.") + w.Line("// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig") + w.Line("const std = @import(\"std\");") + w.Line("const koalabear = @import(\"koalabear\");") + w.Blank() + w.Line("const ModuleSize: usize = %d;", g.gv.n) + w.Line("const Ext = koalabear.Ext;") + w.Line("const Gen = koalabear.Gen;") + w.Line("const f = koalabear.f;") + w.Blank() + + g.writeScalarSnapshot() + if err := g.writeExpressionEvaluators(); err != nil { + return nil, err + } + g.writeCheckFunction() + g.writeVerifyFunction() + g.writeTest() + + return []byte(w.String()), nil +} + +func (g *zigVerifierGenerator) writeScalarSnapshot() { + w := &g.out + w.Line("const eval_coin = %s;", zigGenLiteral(g.rt.GetCoinValue(g.gv.evalCoin))) + w.Line("const merge_coin = %s;", zigGenLiteral(g.rt.GetCoinValue(g.gv.mergeCoin))) + + for i, claim := range g.gv.witnessClaims { + w.Line("const witness_eval_%d = %s;", i, zigGenLiteral(g.rt.GetCellValue(claim))) + } + + for i, bkt := range g.gv.buckets { + for k, claim := range bkt.quotientClaims { + w.Line("const quotient_claim_b%d_s%d = %s;", i, k, zigGenLiteral(g.rt.GetCellValue(claim))) + } + for j, v := range bkt.vanishings { + w.Line("const cancelled_b%d_v%d = [_]i64{%s};", i, j, zigIntList(v.CancelledPositions)) + } + } + w.Blank() +} + +func (g *zigVerifierGenerator) writeExpressionEvaluators() error { + w := &g.out + for i, bkt := range g.gv.buckets { + for j, v := range bkt.vanishings { + expr, err := g.zigExpr(v.Expression) + if err != nil { + return fmt.Errorf("wiop/compilers: generate Zig expression for bucket %d vanishing %d: %w", i, j, err) + } + w.Line("fn evalVanishing_b%d_v%d() Gen {", i, j) + w.In() + w.Line("return %s;", expr) + w.Out() + w.Line("}") + w.Blank() + } + } + return nil +} + +func (g *zigVerifierGenerator) writeCheckFunction() { + w := &g.out + w.Line("pub fn check() !void {") + w.In() + w.Line("const r = eval_coin;") + w.Line("const coin_ext = merge_coin.asExt();") + w.Line("const annihilator = koalabear.computeAnnihilator(r, ModuleSize);") + w.Blank() + + for i, bkt := range g.gv.buckets { + g.writeBucketCheck(i, bkt) + if i != len(g.gv.buckets)-1 { + w.Blank() + } + } + + w.Out() + w.Line("}") + w.Blank() +} + +func (g *zigVerifierGenerator) writeBucketCheck(i int, bkt verifierBucket) { + w := &g.out + w.Line("{") + w.In() + w.Line("// Bucket %d, quotient ratio %d.", i, bkt.ratio) + w.Line("var qr = Gen.zero();") + w.Line("var r_pow_kn = Gen.one();") + w.Line("const r_pow_n = koalabear.expFieldElem(r, ModuleSize);") + for k := range bkt.quotientClaims { + w.Line("qr = qr.add(r_pow_kn.mul(quotient_claim_b%d_s%d));", i, k) + w.Line("r_pow_kn = r_pow_kn.mul(r_pow_n);") + } + w.Blank() + w.Line("var pagg = Gen.zero();") + w.Line("var coin_pow = Ext.one();") + for j := range bkt.vanishings { + w.Line("{") + w.In() + w.Line("const pr = evalVanishing_b%d_v%d();", i, j) + w.Line("const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b%d_v%d);", i, j) + w.Line("const p_times_c = pr.mul(cr);") + w.Line("const term = if (p_times_c.isBase())") + w.In() + w.Line("coin_pow.mulByField(p_times_c.asBase())") + w.Out() + w.Line("else") + w.In() + w.Line("coin_pow.mul(p_times_c.asExt());") + w.Out() + w.Line("pagg = pagg.add(Gen.fromExt(term));") + w.Line("coin_pow = coin_pow.mul(coin_ext);") + w.Out() + w.Line("}") + } + w.Blank() + w.Line("const lhs = pagg;") + w.Line("const rhs = annihilator.mul(qr);") + w.Line("if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed;") + w.Out() + w.Line("}") +} + +func (g *zigVerifierGenerator) writeVerifyFunction() { + w := &g.out + w.Line("pub fn verify() bool {") + w.In() + w.Line("check() catch return false;") + w.Line("return true;") + w.Out() + w.Line("}") + w.Blank() +} + +func (g *zigVerifierGenerator) writeTest() { + w := &g.out + w.Line("test \"generated global verifier accepts runtime snapshot\" {") + w.In() + w.Line("try check();") + w.Line("try std.testing.expect(verify());") + w.Out() + w.Line("}") +} + +func (g *zigVerifierGenerator) zigExpr(expr wiop.Expression) (string, error) { + switch e := expr.(type) { + case *wiop.ColumnView: + key := colViewKey{id: e.Column.Context.ID, shift: e.ShiftingOffset} + name, ok := g.witnessNames[key] + if !ok { + return "", fmt.Errorf("missing witness claim for column %q shift %d", e.Column.Context.Path(), e.ShiftingOffset) + } + return name, nil + case *wiop.ArithmeticOperation: + operands := make([]string, len(e.Operands)) + for i, child := range e.Operands { + childExpr, err := g.zigExpr(child) + if err != nil { + return "", err + } + operands[i] = childExpr + } + return zigArithmeticExpr(e.Operator, operands) + case *wiop.Constant: + return fmt.Sprintf("Gen.base(%s)", zigFieldLiteral(e.Value)), nil + case *wiop.CoinField: + return zigGenLiteral(g.rt.GetCoinValue(e)), nil + case *wiop.Cell: + return zigGenLiteral(g.rt.GetCellValue(e)), nil + default: + return "", fmt.Errorf("unsupported expression type %T", expr) + } +} + +func zigArithmeticExpr(op wiop.ArithmeticOperator, operands []string) (string, error) { + switch op { + case wiop.ArithmeticOperatorAdd: + return fmt.Sprintf("%s.add(%s)", operands[0], operands[1]), nil + case wiop.ArithmeticOperatorSub: + return fmt.Sprintf("%s.sub(%s)", operands[0], operands[1]), nil + case wiop.ArithmeticOperatorMul: + return fmt.Sprintf("%s.mul(%s)", operands[0], operands[1]), nil + case wiop.ArithmeticOperatorDiv: + return fmt.Sprintf("%s.div(%s)", operands[0], operands[1]), nil + case wiop.ArithmeticOperatorDouble: + return fmt.Sprintf("%s.add(%s)", operands[0], operands[0]), nil + case wiop.ArithmeticOperatorSquare: + return fmt.Sprintf("%s.square()", operands[0]), nil + case wiop.ArithmeticOperatorNegate: + return fmt.Sprintf("%s.neg()", operands[0]), nil + case wiop.ArithmeticOperatorInverse: + return fmt.Sprintf("%s.inverse()", operands[0]), nil + default: + return "", fmt.Errorf("unsupported arithmetic operator %s", op) + } +} + +func zigGenLiteral(v field.Gen) string { + if v.IsBase() { + return fmt.Sprintf("Gen.base(%s)", zigFieldLiteral(v.AsBase())) + } + ext := v.AsExt() + c0, c1, c2, c3 := field.ExtToUint64s(&ext) + return fmt.Sprintf( + "Gen.fromExt(Ext.init(f(%d), f(%d), f(%d), f(%d)))", + c0, c1, c2, c3, + ) +} + +func zigFieldLiteral(v field.Element) string { + return fmt.Sprintf("f(%d)", v.Uint64()) +} + +func zigIntList(values []int) string { + if len(values) == 0 { + return "" + } + if len(values) == 1 { + return fmt.Sprintf("%d", values[0]) + } + parts := make([]string, len(values)) + for i, value := range values { + parts[i] = fmt.Sprintf("%d", value) + } + return " " + strings.Join(parts, ", ") + " " +} + +type zigWriter struct { + buf strings.Builder + indent int +} + +func (w *zigWriter) Line(format string, args ...any) { + fmt.Fprintf(&w.buf, "%s%s\n", strings.Repeat(" ", w.indent), fmt.Sprintf(format, args...)) +} + +func (w *zigWriter) Blank() { + w.buf.WriteByte('\n') +} + +func (w *zigWriter) In() { + w.indent++ +} + +func (w *zigWriter) Out() { + w.indent-- +} + +func (w *zigWriter) String() string { + return w.buf.String() +} diff --git a/prover-ray/wiop/query_lagrange_eval.go b/prover-ray/wiop/query_lagrange_eval.go index 423c19b0cd8..008e3003e77 100644 --- a/prover-ray/wiop/query_lagrange_eval.go +++ b/prover-ray/wiop/query_lagrange_eval.go @@ -16,7 +16,7 @@ import ( // // For a column C of size n and evaluation point X, the evaluation is: // -// (X^n − 1) / n · Σ_{i= Modulus) sum - Modulus else sum }; + } + + pub fn sub(self: Field, other: Field) Field { + if (self.value >= other.value) { + return .{ .value = self.value - other.value }; + } + return .{ .value = self.value + Modulus - other.value }; + } + + pub fn mul(self: Field, other: Field) Field { + return .{ .value = (self.value * other.value) % Modulus }; + } + + pub fn neg(self: Field) Field { + return if (self.value == 0) self else .{ .value = Modulus - self.value }; + } + + pub fn square(self: Field) Field { + return self.mul(self); + } + + pub fn pow(self: Field, exponent: u64) Field { + var result = f(1); + var base = self; + var e = exponent; + while (e != 0) : (e >>= 1) { + if ((e & 1) == 1) result = result.mul(base); + base = base.square(); + } + return result; + } + + pub fn inv(self: Field) Field { + return self.pow(Modulus - 2); + } + + pub fn div(self: Field, other: Field) Field { + return self.mul(other.inv()); + } + + pub fn isZero(self: Field) bool { + return self.value == 0; + } +}; + +pub fn f(v: u64) Field { + return Field.init(v); +} + +pub const Ext = struct { + c0: Field, + c1: Field, + c2: Field, + c3: Field, + + pub fn init(c0: Field, c1: Field, c2: Field, c3: Field) Ext { + return .{ .c0 = c0, .c1 = c1, .c2 = c2, .c3 = c3 }; + } + + pub fn zero() Ext { + return init(f(0), f(0), f(0), f(0)); + } + + pub fn one() Ext { + return init(f(1), f(0), f(0), f(0)); + } + + pub fn fromBase(x: Field) Ext { + return init(x, f(0), f(0), f(0)); + } + + pub fn add(self: Ext, other: Ext) Ext { + return init( + self.c0.add(other.c0), + self.c1.add(other.c1), + self.c2.add(other.c2), + self.c3.add(other.c3), + ); + } + + pub fn sub(self: Ext, other: Ext) Ext { + return init( + self.c0.sub(other.c0), + self.c1.sub(other.c1), + self.c2.sub(other.c2), + self.c3.sub(other.c3), + ); + } + + pub fn neg(self: Ext) Ext { + return init(self.c0.neg(), self.c1.neg(), self.c2.neg(), self.c3.neg()); + } + + pub fn mulByField(self: Ext, scalar: Field) Ext { + return init( + self.c0.mul(scalar), + self.c1.mul(scalar), + self.c2.mul(scalar), + self.c3.mul(scalar), + ); + } + + pub fn mul(self: Ext, other: Ext) Ext { + const three = f(3); + + // Tower representation: c0 + c1*u + c2*v + c3*u*v, + // with u^2 = 3 and v^2 = u. + const c0 = self.c0.mul(other.c0) + .add(three.mul(self.c1.mul(other.c1))) + .add(three.mul(self.c2.mul(other.c3))) + .add(three.mul(self.c3.mul(other.c2))); + + const c1 = self.c0.mul(other.c1) + .add(self.c1.mul(other.c0)) + .add(self.c2.mul(other.c2)) + .add(three.mul(self.c3.mul(other.c3))); + + const c2 = self.c0.mul(other.c2) + .add(three.mul(self.c1.mul(other.c3))) + .add(self.c2.mul(other.c0)) + .add(three.mul(self.c3.mul(other.c1))); + + const c3 = self.c0.mul(other.c3) + .add(self.c1.mul(other.c2)) + .add(self.c2.mul(other.c1)) + .add(self.c3.mul(other.c0)); + + return init(c0, c1, c2, c3); + } + + pub fn square(self: Ext) Ext { + return self.mul(self); + } + + pub fn pow(self: Ext, exponent: u128) Ext { + var result = Ext.one(); + var base = self; + var e = exponent; + while (e != 0) : (e >>= 1) { + if ((e & 1) == 1) result = result.mul(base); + base = base.square(); + } + return result; + } + + pub fn inv(self: Ext) Ext { + const p = @as(u128, Modulus); + return self.pow((p * p * p * p) - 2); + } + + pub fn div(self: Ext, other: Ext) Ext { + return self.mul(other.inv()); + } + + pub fn isZero(self: Ext) bool { + return self.c0.isZero() and self.c1.isZero() and self.c2.isZero() and self.c3.isZero(); + } +}; + +pub const Gen = struct { + ext: Ext, + is_base: bool, + + pub fn base(x: Field) Gen { + return .{ .ext = Ext.fromBase(x), .is_base = true }; + } + + pub fn fromExt(x: Ext) Gen { + return .{ .ext = x, .is_base = false }; + } + + pub fn zero() Gen { + return base(f(0)); + } + + pub fn one() Gen { + return base(f(1)); + } + + pub fn isBase(self: Gen) bool { + return self.is_base; + } + + pub fn asBase(self: Gen) Field { + if (!self.is_base) @panic("Gen.asBase called on an extension element"); + return self.ext.c0; + } + + pub fn asExt(self: Gen) Ext { + return self.ext; + } + + pub fn add(self: Gen, other: Gen) Gen { + return .{ + .ext = self.ext.add(other.ext), + .is_base = self.is_base and other.is_base, + }; + } + + pub fn sub(self: Gen, other: Gen) Gen { + return .{ + .ext = self.ext.sub(other.ext), + .is_base = self.is_base and other.is_base, + }; + } + + pub fn mul(self: Gen, other: Gen) Gen { + if (self.is_base and other.is_base) { + return base(self.ext.c0.mul(other.ext.c0)); + } + if (self.is_base) { + return fromExt(other.ext.mulByField(self.ext.c0)); + } + if (other.is_base) { + return fromExt(self.ext.mulByField(other.ext.c0)); + } + return fromExt(self.ext.mul(other.ext)); + } + + pub fn neg(self: Gen) Gen { + return .{ .ext = self.ext.neg(), .is_base = self.is_base }; + } + + pub fn square(self: Gen) Gen { + if (self.is_base) { + return base(self.ext.c0.square()); + } + return fromExt(self.ext.square()); + } + + pub fn inverse(self: Gen) Gen { + if (self.is_base) { + return base(self.ext.c0.inv()); + } + return fromExt(self.ext.inv()); + } + + pub fn div(self: Gen, other: Gen) Gen { + return self.mul(other.inverse()); + } + + pub fn isZero(self: Gen) bool { + if (self.is_base) return self.ext.c0.isZero(); + return self.ext.isZero(); + } +}; + +pub fn expFieldElem(base: Gen, exp: usize) Gen { + var result = Gen.one(); + var b = base; + var e = exp; + while (e != 0) : (e >>= 1) { + if ((e & 1) == 1) result = result.mul(b); + b = b.square(); + } + return result; +} + +pub fn expFieldInt(base: Field, exp: i64) Field { + if (exp < 0) { + return base.inv().pow(@as(u64, @intCast(-exp))); + } + return base.pow(@as(u64, @intCast(exp))); +} + +pub fn computeAnnihilator(r: Gen, n: usize) Gen { + return expFieldElem(r, n).sub(Gen.one()); +} + +pub fn rootOfUnityBy(n: usize) Field { + if (n == 0 or !isPowerOfTwo(n)) { + @panic("n must be positive and a power of two"); + } + + const log_n = log2PowerOfTwo(n); + if (log_n > MaxOrderRoot) { + @panic("requested root of unity is too large"); + } + + var res = f(RootOfUnityValue); + var i = log_n; + while (i < MaxOrderRoot) : (i += 1) { + res = res.square(); + } + return res; +} + +pub fn evalCancellationAtPoint(comptime n: usize, r: Gen, cancelled: []const i64) Gen { + if (cancelled.len == 0) return Gen.one(); + + const omega = rootOfUnityBy(n); + var result = Gen.one(); + for (cancelled) |pos| { + const norm = if (pos < 0) @as(i64, @intCast(n)) + pos else pos; + const omega_k = expFieldInt(omega, norm); + result = result.mul(r.sub(Gen.base(omega_k))); + } + return result; +} + +fn isPowerOfTwo(n: usize) bool { + return n != 0 and (n & (n - 1)) == 0; +} + +fn log2PowerOfTwo(n: usize) usize { + var x = n; + var log: usize = 0; + while (x > 1) : (x >>= 1) { + log += 1; + } + return log; +} + +test "field arithmetic" { + try std.testing.expectEqual(@as(u64, 3), f(1).add(f(2)).value); + try std.testing.expectEqual(@as(u64, Modulus - 1), f(1).sub(f(2)).value); + try std.testing.expectEqual(@as(u64, 6), f(2).mul(f(3)).value); + try std.testing.expectEqual(@as(u64, Modulus - 1), f(1).neg().value); + try std.testing.expectEqual(@as(u64, 1), f(7).div(f(7)).value); +} + +test "extension arithmetic" { + const a = Ext.init(f(1), f(2), f(3), f(4)); + const b = Ext.init(f(5), f(6), f(7), f(8)); + const product = a.mul(b); + + try std.testing.expectEqual(@as(u64, 197), product.c0.value); + try std.testing.expectEqual(@as(u64, 133), product.c1.value); + try std.testing.expectEqual(@as(u64, 142), product.c2.value); + try std.testing.expectEqual(@as(u64, 60), product.c3.value); + try std.testing.expect(a.mul(a.inv()).sub(Ext.one()).isZero()); +} + +test "root helpers" { + const omega_8 = rootOfUnityBy(8); + try std.testing.expect(omega_8.pow(8).sub(f(1)).isZero()); + try std.testing.expect(rootOfUnityBy(16).square().sub(omega_8).isZero()); +} + +test "generic field helpers" { + const r = Gen.fromExt(Ext.init(f(2), f(3), f(4), f(5))); + try std.testing.expect(expFieldElem(r, 0).sub(Gen.one()).isZero()); + try std.testing.expect(expFieldElem(r, 3).sub(r.mul(r).mul(r)).isZero()); + + const cancelled = [_]i64{ 0, -1 }; + const c = evalCancellationAtPoint(8, Gen.base(f(9)), &cancelled); + const expected = Gen.base(f(9).sub(rootOfUnityBy(8).pow(0))) + .mul(Gen.base(f(9).sub(rootOfUnityBy(8).pow(7)))); + try std.testing.expect(c.sub(expected).isZero()); +} diff --git a/prover-ray/wiop/zigverifiers/global_verifier_booleancolumn.zig b/prover-ray/wiop/zigverifiers/global_verifier_booleancolumn.zig new file mode 100644 index 00000000000..4d1374fb953 --- /dev/null +++ b/prover-ray/wiop/zigverifiers/global_verifier_booleancolumn.zig @@ -0,0 +1,62 @@ +// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT. +// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig +const std = @import("std"); +const koalabear = @import("koalabear"); + +const ModuleSize: usize = 4; +const Ext = koalabear.Ext; +const Gen = koalabear.Gen; +const f = koalabear.f; + +const eval_coin = Gen.fromExt(Ext.init(f(2060204470), f(1956716771), f(1756251810), f(615546))); +const merge_coin = Gen.fromExt(Ext.init(f(1474885422), f(1349438218), f(1151436381), f(1251922572))); +const witness_eval_0 = Gen.fromExt(Ext.init(f(519072325), f(1514525194), f(1556160019), f(581407897))); +const quotient_claim_b0_s0 = Gen.fromExt(Ext.init(f(1598029825), f(0), f(0), f(0))); +const cancelled_b0_v0 = [_]i64{}; + +fn evalVanishing_b0_v0() Gen { + return witness_eval_0.mul(witness_eval_0).sub(witness_eval_0); +} + +pub fn check() !void { + const r = eval_coin; + const coin_ext = merge_coin.asExt(); + const annihilator = koalabear.computeAnnihilator(r, ModuleSize); + + { + // Bucket 0, quotient ratio 1. + var qr = Gen.zero(); + var r_pow_kn = Gen.one(); + const r_pow_n = koalabear.expFieldElem(r, ModuleSize); + qr = qr.add(r_pow_kn.mul(quotient_claim_b0_s0)); + r_pow_kn = r_pow_kn.mul(r_pow_n); + + var pagg = Gen.zero(); + var coin_pow = Ext.one(); + { + const pr = evalVanishing_b0_v0(); + const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b0_v0); + const p_times_c = pr.mul(cr); + const term = if (p_times_c.isBase()) + coin_pow.mulByField(p_times_c.asBase()) + else + coin_pow.mul(p_times_c.asExt()); + pagg = pagg.add(Gen.fromExt(term)); + coin_pow = coin_pow.mul(coin_ext); + } + + const lhs = pagg; + const rhs = annihilator.mul(qr); + if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed; + } +} + +pub fn verify() bool { + check() catch return false; + return true; +} + +test "generated global verifier accepts runtime snapshot" { + try check(); + try std.testing.expect(verify()); +} diff --git a/prover-ray/wiop/zigverifiers/global_verifier_conditionalcounter.zig b/prover-ray/wiop/zigverifiers/global_verifier_conditionalcounter.zig new file mode 100644 index 00000000000..51d95fc997d --- /dev/null +++ b/prover-ray/wiop/zigverifiers/global_verifier_conditionalcounter.zig @@ -0,0 +1,64 @@ +// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT. +// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig +const std = @import("std"); +const koalabear = @import("koalabear"); + +const ModuleSize: usize = 8; +const Ext = koalabear.Ext; +const Gen = koalabear.Gen; +const f = koalabear.f; + +const eval_coin = Gen.fromExt(Ext.init(f(1153697193), f(391303323), f(1365701028), f(1747609975))); +const merge_coin = Gen.fromExt(Ext.init(f(1214173699), f(285770722), f(1560241999), f(697020807))); +const witness_eval_0 = Gen.fromExt(Ext.init(f(1696294061), f(784116686), f(1363654080), f(441171688))); +const witness_eval_1 = Gen.fromExt(Ext.init(f(798952290), f(871645908), f(1936591634), f(1083653620))); +const witness_eval_2 = Gen.fromExt(Ext.init(f(1576420554), f(238890163), f(2116992978), f(1710452387))); +const quotient_claim_b0_s0 = Gen.fromExt(Ext.init(f(799014912), f(0), f(0), f(0))); +const cancelled_b0_v0 = [_]i64{0}; + +fn evalVanishing_b0_v0() Gen { + return witness_eval_0.sub(witness_eval_1).sub(witness_eval_2); +} + +pub fn check() !void { + const r = eval_coin; + const coin_ext = merge_coin.asExt(); + const annihilator = koalabear.computeAnnihilator(r, ModuleSize); + + { + // Bucket 0, quotient ratio 1. + var qr = Gen.zero(); + var r_pow_kn = Gen.one(); + const r_pow_n = koalabear.expFieldElem(r, ModuleSize); + qr = qr.add(r_pow_kn.mul(quotient_claim_b0_s0)); + r_pow_kn = r_pow_kn.mul(r_pow_n); + + var pagg = Gen.zero(); + var coin_pow = Ext.one(); + { + const pr = evalVanishing_b0_v0(); + const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b0_v0); + const p_times_c = pr.mul(cr); + const term = if (p_times_c.isBase()) + coin_pow.mulByField(p_times_c.asBase()) + else + coin_pow.mul(p_times_c.asExt()); + pagg = pagg.add(Gen.fromExt(term)); + coin_pow = coin_pow.mul(coin_ext); + } + + const lhs = pagg; + const rhs = annihilator.mul(qr); + if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed; + } +} + +pub fn verify() bool { + check() catch return false; + return true; +} + +test "generated global verifier accepts runtime snapshot" { + try check(); + try std.testing.expect(verify()); +} diff --git a/prover-ray/wiop/zigverifiers/global_verifier_fibonacci.zig b/prover-ray/wiop/zigverifiers/global_verifier_fibonacci.zig new file mode 100644 index 00000000000..42614455242 --- /dev/null +++ b/prover-ray/wiop/zigverifiers/global_verifier_fibonacci.zig @@ -0,0 +1,64 @@ +// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT. +// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig +const std = @import("std"); +const koalabear = @import("koalabear"); + +const ModuleSize: usize = 8; +const Ext = koalabear.Ext; +const Gen = koalabear.Gen; +const f = koalabear.f; + +const eval_coin = Gen.fromExt(Ext.init(f(1263253473), f(254241140), f(182581878), f(1536911770))); +const merge_coin = Gen.fromExt(Ext.init(f(1907912801), f(1075719925), f(2034060891), f(797530886))); +const witness_eval_0 = Gen.fromExt(Ext.init(f(702747524), f(1229725543), f(256620939), f(144381248))); +const witness_eval_1 = Gen.fromExt(Ext.init(f(1107722545), f(1937035519), f(1672116809), f(1603174440))); +const witness_eval_2 = Gen.fromExt(Ext.init(f(323767309), f(525773112), f(385325547), f(537148890))); +const quotient_claim_b0_s0 = Gen.fromExt(Ext.init(f(1922147795), f(22607859), f(507605100), f(2088734547))); +const cancelled_b0_v0 = [_]i64{ 0, 1 }; + +fn evalVanishing_b0_v0() Gen { + return witness_eval_0.sub(witness_eval_1).sub(witness_eval_2); +} + +pub fn check() !void { + const r = eval_coin; + const coin_ext = merge_coin.asExt(); + const annihilator = koalabear.computeAnnihilator(r, ModuleSize); + + { + // Bucket 0, quotient ratio 1. + var qr = Gen.zero(); + var r_pow_kn = Gen.one(); + const r_pow_n = koalabear.expFieldElem(r, ModuleSize); + qr = qr.add(r_pow_kn.mul(quotient_claim_b0_s0)); + r_pow_kn = r_pow_kn.mul(r_pow_n); + + var pagg = Gen.zero(); + var coin_pow = Ext.one(); + { + const pr = evalVanishing_b0_v0(); + const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b0_v0); + const p_times_c = pr.mul(cr); + const term = if (p_times_c.isBase()) + coin_pow.mulByField(p_times_c.asBase()) + else + coin_pow.mul(p_times_c.asExt()); + pagg = pagg.add(Gen.fromExt(term)); + coin_pow = coin_pow.mul(coin_ext); + } + + const lhs = pagg; + const rhs = annihilator.mul(qr); + if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed; + } +} + +pub fn verify() bool { + check() catch return false; + return true; +} + +test "generated global verifier accepts runtime snapshot" { + try check(); + try std.testing.expect(verify()); +} diff --git a/prover-ray/wiop/zigverifiers/global_verifier_geometricprogression.zig b/prover-ray/wiop/zigverifiers/global_verifier_geometricprogression.zig new file mode 100644 index 00000000000..797676c3297 --- /dev/null +++ b/prover-ray/wiop/zigverifiers/global_verifier_geometricprogression.zig @@ -0,0 +1,63 @@ +// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT. +// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig +const std = @import("std"); +const koalabear = @import("koalabear"); + +const ModuleSize: usize = 8; +const Ext = koalabear.Ext; +const Gen = koalabear.Gen; +const f = koalabear.f; + +const eval_coin = Gen.fromExt(Ext.init(f(452200495), f(105798452), f(2072991977), f(1535330589))); +const merge_coin = Gen.fromExt(Ext.init(f(1230103960), f(1020467545), f(1621649414), f(199619948))); +const witness_eval_0 = Gen.fromExt(Ext.init(f(1033066955), f(548641793), f(1772433870), f(281279814))); +const witness_eval_1 = Gen.fromExt(Ext.init(f(1257457816), f(1005353794), f(208876584), f(1629736094))); +const quotient_claim_b0_s0 = Gen.fromExt(Ext.init(f(1864368097), f(0), f(0), f(0))); +const cancelled_b0_v0 = [_]i64{0}; + +fn evalVanishing_b0_v0() Gen { + return witness_eval_0.sub(Gen.base(f(2)).mul(witness_eval_1)); +} + +pub fn check() !void { + const r = eval_coin; + const coin_ext = merge_coin.asExt(); + const annihilator = koalabear.computeAnnihilator(r, ModuleSize); + + { + // Bucket 0, quotient ratio 1. + var qr = Gen.zero(); + var r_pow_kn = Gen.one(); + const r_pow_n = koalabear.expFieldElem(r, ModuleSize); + qr = qr.add(r_pow_kn.mul(quotient_claim_b0_s0)); + r_pow_kn = r_pow_kn.mul(r_pow_n); + + var pagg = Gen.zero(); + var coin_pow = Ext.one(); + { + const pr = evalVanishing_b0_v0(); + const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b0_v0); + const p_times_c = pr.mul(cr); + const term = if (p_times_c.isBase()) + coin_pow.mulByField(p_times_c.asBase()) + else + coin_pow.mul(p_times_c.asExt()); + pagg = pagg.add(Gen.fromExt(term)); + coin_pow = coin_pow.mul(coin_ext); + } + + const lhs = pagg; + const rhs = annihilator.mul(qr); + if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed; + } +} + +pub fn verify() bool { + check() catch return false; + return true; +} + +test "generated global verifier accepts runtime snapshot" { + try check(); + try std.testing.expect(verify()); +} diff --git a/prover-ray/wiop/zigverifiers/global_verifier_pythagoreantriplet.zig b/prover-ray/wiop/zigverifiers/global_verifier_pythagoreantriplet.zig new file mode 100644 index 00000000000..00efaf14c20 --- /dev/null +++ b/prover-ray/wiop/zigverifiers/global_verifier_pythagoreantriplet.zig @@ -0,0 +1,64 @@ +// Code generated by wiop/compilers/global.Verifier.GenerateZig. DO NOT EDIT. +// Compile with: zig test --dep koalabear -Mroot= -Mkoalabear=wiop/zigstatics/koalabear_field.zig +const std = @import("std"); +const koalabear = @import("koalabear"); + +const ModuleSize: usize = 8; +const Ext = koalabear.Ext; +const Gen = koalabear.Gen; +const f = koalabear.f; + +const eval_coin = Gen.fromExt(Ext.init(f(2035677098), f(1570926853), f(1442981450), f(1913355614))); +const merge_coin = Gen.fromExt(Ext.init(f(2028014486), f(1497581644), f(1603516956), f(883951024))); +const witness_eval_0 = Gen.fromExt(Ext.init(f(199829467), f(385323256), f(694899864), f(965005248))); +const witness_eval_1 = Gen.fromExt(Ext.init(f(2077605827), f(1047980627), f(1668565355), f(276683834))); +const witness_eval_2 = Gen.fromExt(Ext.init(f(1720843325), f(1679364928), f(945287975), f(1927622668))); +const quotient_claim_b0_s0 = Gen.fromExt(Ext.init(f(81697106), f(364001583), f(1952067355), f(580569005))); +const cancelled_b0_v0 = [_]i64{}; + +fn evalVanishing_b0_v0() Gen { + return witness_eval_0.square().sub(witness_eval_1.square()).sub(witness_eval_2.square()); +} + +pub fn check() !void { + const r = eval_coin; + const coin_ext = merge_coin.asExt(); + const annihilator = koalabear.computeAnnihilator(r, ModuleSize); + + { + // Bucket 0, quotient ratio 1. + var qr = Gen.zero(); + var r_pow_kn = Gen.one(); + const r_pow_n = koalabear.expFieldElem(r, ModuleSize); + qr = qr.add(r_pow_kn.mul(quotient_claim_b0_s0)); + r_pow_kn = r_pow_kn.mul(r_pow_n); + + var pagg = Gen.zero(); + var coin_pow = Ext.one(); + { + const pr = evalVanishing_b0_v0(); + const cr = koalabear.evalCancellationAtPoint(ModuleSize, r, &cancelled_b0_v0); + const p_times_c = pr.mul(cr); + const term = if (p_times_c.isBase()) + coin_pow.mulByField(p_times_c.asBase()) + else + coin_pow.mul(p_times_c.asExt()); + pagg = pagg.add(Gen.fromExt(term)); + coin_pow = coin_pow.mul(coin_ext); + } + + const lhs = pagg; + const rhs = annihilator.mul(qr); + if (!lhs.sub(rhs).isZero()) return error.GlobalQuotientCheckFailed; + } +} + +pub fn verify() bool { + check() catch return false; + return true; +} + +test "generated global verifier accepts runtime snapshot" { + try check(); + try std.testing.expect(verify()); +}