From 2dac7833062f03ed0b6751166cad37de1c8eb56f Mon Sep 17 00:00:00 2001 From: John Guibas Date: Wed, 13 Mar 2024 20:53:06 -0700 Subject: [PATCH] feat: gnark e2e build (#381) Co-authored-by: John Guibas --- recursion/compiler/examples/fibonacci.rs | 1 - recursion/compiler/src/backend/gnark.txt | 13 -- .../src/gnark/lib/babybear/babybear.go | 175 ++++++++++++++++++ recursion/compiler/src/gnark/lib/go.mod | 30 +++ recursion/compiler/src/gnark/lib/go.sum | 63 +++++++ recursion/compiler/src/gnark/lib/main.go | 32 ++++ recursion/compiler/src/gnark/lib/template.txt | 18 ++ .../compiler/src/{backend => gnark}/mod.rs | 138 +++++++++----- recursion/compiler/src/lib.rs | 2 +- 9 files changed, 408 insertions(+), 64 deletions(-) delete mode 100644 recursion/compiler/src/backend/gnark.txt create mode 100644 recursion/compiler/src/gnark/lib/babybear/babybear.go create mode 100644 recursion/compiler/src/gnark/lib/go.mod create mode 100644 recursion/compiler/src/gnark/lib/go.sum create mode 100644 recursion/compiler/src/gnark/lib/main.go create mode 100644 recursion/compiler/src/gnark/lib/template.txt rename recursion/compiler/src/{backend => gnark}/mod.rs (80%) diff --git a/recursion/compiler/examples/fibonacci.rs b/recursion/compiler/examples/fibonacci.rs index 8fe66a4b15..4598d71e8a 100644 --- a/recursion/compiler/examples/fibonacci.rs +++ b/recursion/compiler/examples/fibonacci.rs @@ -3,7 +3,6 @@ use p3_field::AbstractField; use sp1_core::stark::StarkGenericConfig; use sp1_core::utils::BabyBearPoseidon2; use sp1_recursion_compiler::asm::VmBuilder; -use sp1_recursion_compiler::backend::GnarkBackend; use sp1_recursion_compiler::prelude::*; use sp1_recursion_core::runtime::Runtime; diff --git a/recursion/compiler/src/backend/gnark.txt b/recursion/compiler/src/backend/gnark.txt deleted file mode 100644 index 9b267b6fbe..0000000000 --- a/recursion/compiler/src/backend/gnark.txt +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import "github.com/consensys/gnark/frontend" - -type Circuit struct { - X frontend.Variable `gnark:"x"` // x --> secret visibility (default) - Y frontend.Variable `gnark:",public"` // Y --> public visibility -} - -func (circuit *Circuit) Define(api frontend.API) error { -{{LINES}} - return nil -} diff --git a/recursion/compiler/src/gnark/lib/babybear/babybear.go b/recursion/compiler/src/gnark/lib/babybear/babybear.go new file mode 100644 index 0000000000..00fd5931e3 --- /dev/null +++ b/recursion/compiler/src/gnark/lib/babybear/babybear.go @@ -0,0 +1,175 @@ +package babybear + +import ( + "math/big" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" +) + +var Modulus = new(big.Int).SetUint64(2013265921) + +type Params struct{} + +func (fp Params) NbLimbs() uint { return 1 } +func (fp Params) BitsPerLimb() uint { return 32 } +func (fp Params) IsPrime() bool { return true } +func (fp Params) Modulus() *big.Int { return Modulus } +func (fp Params) NumElmsPerBN254Elm() uint { return 8 } + +type Variable struct { + value *emulated.Element[Params] +} + +type ExtensionVariable struct { + value [4]*Variable +} + +type Chip struct { + api frontend.API + field *emulated.Field[Params] +} + +func NewChip(api frontend.API) *Chip { + field, err := emulated.NewField[Params](api) + if err != nil { + panic(err) + } + return &Chip{ + api: api, + field: field, + } +} + +func NewVariable(value int) *Variable { + variable := emulated.ValueOf[Params](value) + return &Variable{ + value: &variable, + } +} + +func NewExtensionVariable(value [4]int) *ExtensionVariable { + a := NewVariable(value[0]) + b := NewVariable(value[1]) + c := NewVariable(value[2]) + d := NewVariable(value[3]) + return &ExtensionVariable{value: [4]*Variable{a, b, c, d}} +} + +func (c *Chip) Add(a, b *Variable) *Variable { + return &Variable{ + value: c.field.Add(a.value, b.value), + } +} + +func (c *Chip) Sub(a, b *Variable) *Variable { + return &Variable{ + value: c.field.Sub(a.value, b.value), + } +} + +func (c *Chip) Mul(a, b *Variable) *Variable { + return &Variable{ + value: c.field.Mul(a.value, b.value), + } +} + +func (c *Chip) Neg(a *Variable) *Variable { + return &Variable{ + value: c.field.Neg(a.value), + } +} + +func (c *Chip) Inv(a *Variable) *Variable { + return &Variable{ + value: c.field.Inverse(a.value), + } +} + +func (c *Chip) AssertEq(a, b *Variable) { + c.field.AssertIsEqual(a.value, b.value) +} + +func (c *Chip) AssertNe(a, b *Variable) { + diff := c.field.Sub(a.value, b.value) + isZero := c.field.IsZero(diff) + c.api.AssertIsEqual(isZero, frontend.Variable(0)) +} + +func (c *Chip) AddExtension(a, b *ExtensionVariable) *ExtensionVariable { + v1 := c.Add(a.value[0], b.value[0]) + v2 := c.Add(a.value[1], b.value[1]) + v3 := c.Add(a.value[2], b.value[2]) + v4 := c.Add(a.value[3], b.value[3]) + return &ExtensionVariable{value: [4]*Variable{v1, v2, v3, v4}} +} + +func (c *Chip) SubExtension(a, b *ExtensionVariable) *ExtensionVariable { + v1 := c.Sub(a.value[0], b.value[0]) + v2 := c.Sub(a.value[1], b.value[1]) + v3 := c.Sub(a.value[2], b.value[2]) + v4 := c.Sub(a.value[3], b.value[3]) + return &ExtensionVariable{value: [4]*Variable{v1, v2, v3, v4}} +} + +func (c *Chip) MulExtension(a, b *ExtensionVariable) *ExtensionVariable { + w := NewVariable(11) + v := [4]*Variable{ + NewVariable(0), + NewVariable(0), + NewVariable(0), + NewVariable(0), + } + + for i := 0; i < 4; i++ { + for j := 0; j < 4; j++ { + if i+j >= 4 { + v[i+j-4] = c.Add(v[i+j-4], c.Mul(c.Mul(v[i], v[j]), w)) + } else { + v[i+j] = c.Add(v[i+j], c.Mul(v[i], v[j])) + } + } + } + + return &ExtensionVariable{value: v} +} + +func (c *Chip) NegExtension(a *ExtensionVariable) *ExtensionVariable { + v1 := c.Neg(a.value[0]) + v2 := c.Neg(a.value[1]) + v3 := c.Neg(a.value[2]) + v4 := c.Neg(a.value[3]) + return &ExtensionVariable{value: [4]*Variable{v1, v2, v3, v4}} +} + +func (c *Chip) InvExtension(a *ExtensionVariable) *ExtensionVariable { + v := [4]*Variable{ + NewVariable(0), + NewVariable(0), + NewVariable(0), + NewVariable(0), + } + return &ExtensionVariable{value: v} +} + +func (c *Chip) AssertEqExtension(a, b *ExtensionVariable) { + c.AssertEq(a.value[0], b.value[0]) + c.AssertEq(a.value[1], b.value[1]) + c.AssertEq(a.value[2], b.value[2]) + c.AssertEq(a.value[3], b.value[3]) +} + +func (c *Chip) AssertNeExtension(a, b *ExtensionVariable) { + v1 := c.field.Sub(a.value[0].value, b.value[0].value) + v2 := c.field.Sub(a.value[1].value, b.value[1].value) + v3 := c.field.Sub(a.value[2].value, b.value[2].value) + v4 := c.field.Sub(a.value[3].value, b.value[3].value) + isZero1 := c.field.IsZero(v1) + isZero2 := c.field.IsZero(v2) + isZero3 := c.field.IsZero(v3) + isZero4 := c.field.IsZero(v4) + isZero1AndZero2 := c.api.And(isZero1, isZero2) + isZero3AndZero4 := c.api.And(isZero3, isZero4) + isZeroAll := c.api.And(isZero1AndZero2, isZero3AndZero4) + c.api.AssertIsEqual(isZeroAll, frontend.Variable(0)) +} diff --git a/recursion/compiler/src/gnark/lib/go.mod b/recursion/compiler/src/gnark/lib/go.mod new file mode 100644 index 0000000000..6fd6012491 --- /dev/null +++ b/recursion/compiler/src/gnark/lib/go.mod @@ -0,0 +1,30 @@ +module github.com/succinctlabs/sp1-recursion-gnark + +go 1.20 + +require ( + github.com/consensys/gnark v0.9.1 + github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb +) + +require ( + github.com/bits-and-blooms/bitset v1.8.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect + github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.30.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/x448/float16 v0.8.4 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) \ No newline at end of file diff --git a/recursion/compiler/src/gnark/lib/go.sum b/recursion/compiler/src/gnark/lib/go.sum new file mode 100644 index 0000000000..b0a6978fb8 --- /dev/null +++ b/recursion/compiler/src/gnark/lib/go.sum @@ -0,0 +1,63 @@ +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark v0.9.1 h1:aTwBp5469MY/2jNrf4ABrqHRW3+JytfkADdw4ZBY7T0= +github.com/consensys/gnark v0.9.1/go.mod h1:udWvWGXnfBE7mn7BsNoGAvZDnUhcONBEtNijvVjfY80= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= \ No newline at end of file diff --git a/recursion/compiler/src/gnark/lib/main.go b/recursion/compiler/src/gnark/lib/main.go new file mode 100644 index 0000000000..5fdeab8190 --- /dev/null +++ b/recursion/compiler/src/gnark/lib/main.go @@ -0,0 +1,32 @@ +// This file is auto-generated by sp1-recursion-compiler. +package main + +import ( + "github.com/consensys/gnark/frontend" + "github.com/succinctlabs/sp1-recursion-gnark/babybear" +) + +type Circuit struct { + X frontend.Variable + Y frontend.Variable +} + +func (circuit *Circuit) Define(api frontend.API) error { + fieldChip := babybear.NewChip(api) + + // Variables. + var felt0 *babybear.Variable + var felt1 *babybear.Variable + var felt2 *babybear.Variable + + // Operations. + felt0 = babybear.NewVariable(0) + felt1 = babybear.NewVariable(1) + for i := 0; i < 12; i++ { + felt2 = fieldChip.Add(felt1, babybear.NewVariable(0)) + felt1 = fieldChip.Add(felt0, felt1) + felt0 = fieldChip.Add(felt2, babybear.NewVariable(0)) + } + fieldChip.AssertEq(felt0, babybear.NewVariable(144)) + return nil +} diff --git a/recursion/compiler/src/gnark/lib/template.txt b/recursion/compiler/src/gnark/lib/template.txt new file mode 100644 index 0000000000..57fcd70988 --- /dev/null +++ b/recursion/compiler/src/gnark/lib/template.txt @@ -0,0 +1,18 @@ +// This file is auto-generated by sp1-recursion-compiler. +package main + +import ( + "github.com/consensys/gnark/frontend" + "github.com/succinctlabs/sp1-recursion-gnark/babybear" +) + +type Circuit struct { + X frontend.Variable + Y frontend.Variable +} + +func (circuit *Circuit) Define(api frontend.API) error { + fieldChip := babybear.NewChip(api) +{{LINES}} + return nil +} diff --git a/recursion/compiler/src/backend/mod.rs b/recursion/compiler/src/gnark/mod.rs similarity index 80% rename from recursion/compiler/src/backend/mod.rs rename to recursion/compiler/src/gnark/mod.rs index 6129d13247..9bbf969a6d 100644 --- a/recursion/compiler/src/backend/mod.rs +++ b/recursion/compiler/src/gnark/mod.rs @@ -3,13 +3,10 @@ use std::collections::HashMap; use crate::ir::{Config, DslIR}; -const GNARK_TEMPLATE: &str = include_str!("gnark.txt"); +const GNARK_TEMPLATE: &str = include_str!("lib/template.txt"); pub fn indent(lines: Vec) -> Vec { - lines - .into_iter() - .map(|x| format!(" {}", x)) - .collect() + lines.into_iter().map(|x| format!("\t{}", x)).collect() } #[derive(Debug, Clone)] @@ -20,12 +17,8 @@ pub struct GnarkBackend { impl GnarkBackend { pub fn assign(&mut self, id: String) -> &str { - if *self.used.get(&id).unwrap_or(&false) { - "=" - } else { - self.used.insert(id.clone(), true); - ":=" - } + self.used.insert(id.clone(), true); + "=" } pub fn emit(&mut self, operations: Vec>) -> Vec { @@ -38,11 +31,21 @@ impl GnarkBackend { } DslIR::ImmFelt(a, b) => { let operator = self.assign(a.id()); - lines.push(format!("{} {} types.Felt({})", a.id(), operator, b)); + lines.push(format!( + "{} {} babybear.NewVariable({})", + a.id(), + operator, + b + )); } DslIR::ImmExt(a, b) => { let operator = self.assign(a.id()); - lines.push(format!("{} {} types.Ext({})", a.id(), operator, b)); + lines.push(format!( + "{} {} babybear.NewExtensionVariable({})", + a.id(), + operator, + b + )); } DslIR::AddV(a, b, c) => { let operator = self.assign(a.id()); @@ -77,7 +80,7 @@ impl GnarkBackend { DslIR::AddFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Add({}, types.NewFelt({}))", + "{} {} fieldChip.Add({}, babybear.NewVariable({}))", a.id(), operator, b.id(), @@ -87,7 +90,7 @@ impl GnarkBackend { DslIR::AddE(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Add({}, {})", + "{} {} fieldChip.AddExtension({}, {})", a.id(), operator, b.id(), @@ -97,7 +100,7 @@ impl GnarkBackend { DslIR::AddEI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Add({}, types.NewExt({}))", + "{} {} fieldChip.AddExtension({}, babybear.NewExtensionVariable({}))", a.id(), operator, b.id(), @@ -107,7 +110,7 @@ impl GnarkBackend { DslIR::AddEFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.AddFelt({}, types.NewFelt({}))", + "{} {} fieldChip.AddFelt({}, babybear.NewVariable({}))", a.id(), operator, b.id(), @@ -117,7 +120,7 @@ impl GnarkBackend { DslIR::AddEFFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.AddFelt(types.NewExt({}), {})", + "{} {} fieldChip.AddFelt(babybear.NewExtensionVariable({}), {})", a.id(), operator, c, @@ -127,7 +130,7 @@ impl GnarkBackend { DslIR::AddEF(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.AddFelt({}, {})", + "{} {} fieldChip.AddFelt({}, {})", a.id(), operator, b.id(), @@ -167,7 +170,7 @@ impl GnarkBackend { DslIR::MulFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Mul({}, types.NewFelt({}))", + "{} {} fieldChip.Mul({}, babybear.NewVariable({}))", a.id(), operator, b.id(), @@ -177,7 +180,7 @@ impl GnarkBackend { DslIR::MulE(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Mul({}, {})", + "{} {} fieldChip.MulExtension({}, {})", a.id(), operator, b.id(), @@ -187,7 +190,7 @@ impl GnarkBackend { DslIR::MulEI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Mul({}, types.NewExt({}))", + "{} {} fieldChip.MulExtension({}, babybear.NewExtensionVariable({}))", a.id(), operator, b.id(), @@ -197,7 +200,7 @@ impl GnarkBackend { DslIR::MulEFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.MulFelt({}, types.NewFelt({}))", + "{} {} fieldChip.MulFelt({}, babybear.NewVariable({}))", a.id(), operator, c, @@ -257,7 +260,7 @@ impl GnarkBackend { DslIR::SubFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Sub({}, types.NewFelt({}))", + "{} {} fieldChip.Sub({}, babybear.NewVariable({}))", a.id(), operator, b.id(), @@ -267,7 +270,7 @@ impl GnarkBackend { DslIR::SubFIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Sub(types.NewFelt({}), {})", + "{} {} fieldChip.Sub(babybear.NewVariable({}), {})", a.id(), operator, b, @@ -277,7 +280,7 @@ impl GnarkBackend { DslIR::SubE(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Sub({}, {})", + "{} {} fieldChip.SubExtension({}, {})", a.id(), operator, b.id(), @@ -287,7 +290,7 @@ impl GnarkBackend { DslIR::SubEI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Sub({}, types.NewExt({}))", + "{} {} fieldChip.SubExtension({}, babybear.NewExtensionVariable({}))", a.id(), operator, b.id(), @@ -297,7 +300,7 @@ impl GnarkBackend { DslIR::SubEIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Sub(types.NewExt({}), {})", + "{} {} fieldChip.SubExtension(babybear.NewExtensionVariable({}), {})", a.id(), operator, b, @@ -307,7 +310,7 @@ impl GnarkBackend { DslIR::SubEFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.SubFelt({}, types.NewFelt({}))", + "{} {} fieldChip.SubFelt({}, babybear.NewVariable({}))", a.id(), operator, c, @@ -317,7 +320,7 @@ impl GnarkBackend { DslIR::SubEFIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.SubFelt(types.NewExt({}), {})", + "{} {} fieldChip.SubFelt(babybear.NewExtensionVariable({}), {})", a.id(), operator, b, @@ -327,7 +330,7 @@ impl GnarkBackend { DslIR::SubEF(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.SubFelt({}, {})", + "{} {} fieldChip.SubFelt({}, {})", a.id(), operator, b.id(), @@ -347,7 +350,7 @@ impl GnarkBackend { DslIR::DivFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Div({}, types.NewFelt({}))", + "{} {} fieldChip.Div({}, babybear.NewVariable({}))", a.id(), operator, b.id(), @@ -357,7 +360,7 @@ impl GnarkBackend { DslIR::DivFIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} fieldChip.Div(types.NewFelt({}), {})", + "{} {} fieldChip.Div(babybear.NewVariable({}), {})", a.id(), operator, b, @@ -367,7 +370,7 @@ impl GnarkBackend { DslIR::DivE(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Div({}, {})", + "{} {} fieldChip.DivExtension({}, {})", a.id(), operator, b.id(), @@ -377,7 +380,7 @@ impl GnarkBackend { DslIR::DivEI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Div({}, types.NewExt({}))", + "{} {} fieldChip.DivExtension({}, babybear.NewExtensionVariable({}))", a.id(), operator, b.id(), @@ -387,7 +390,7 @@ impl GnarkBackend { DslIR::DivEIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Div(types.NewExt({}), {})", + "{} {} fieldChip.DivExtension(babybear.NewExtensionVariable({}), {})", a.id(), operator, b, @@ -397,7 +400,7 @@ impl GnarkBackend { DslIR::DivEFI(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.DivFelt({}, types.NewFelt({}))", + "{} {} fieldChip.DivFelt({}, babybear.NewVariable({}))", a.id(), operator, c, @@ -407,7 +410,7 @@ impl GnarkBackend { DslIR::DivEFIN(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.DivFelt(types.NewExt({}), {})", + "{} {} fieldChip.DivFelt(babybear.NewExtensionVariable({}), {})", a.id(), operator, b, @@ -417,7 +420,7 @@ impl GnarkBackend { DslIR::DivEF(a, b, c) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.DivFelt({}, {})", + "{} {} fieldChip.DivFelt({}, {})", a.id(), operator, b.id(), @@ -435,7 +438,7 @@ impl GnarkBackend { DslIR::NegE(a, b) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Neg({})", + "{} {} fieldChip.NegExtension({})", a.id(), operator, b.id() @@ -452,7 +455,7 @@ impl GnarkBackend { DslIR::InvE(a, b) => { let operator = self.assign(a.id()); lines.push(format!( - "{} {} extensionChip.Inv({})", + "{} {} fieldChip.InvExtension({})", a.id(), operator, b.id() @@ -488,10 +491,18 @@ impl GnarkBackend { lines.push(format!("fieldChip.AssertNe({}, {})", a.id(), b.id())); } DslIR::AssertEqE(a, b) => { - lines.push(format!("extensionChip.AssertEq({}, {})", a.id(), b.id())); + lines.push(format!( + "fieldChip.AssertEqExtension({}, {})", + a.id(), + b.id() + )); } DslIR::AssertNeE(a, b) => { - lines.push(format!("extensionChip.AssertNe({}, {})", a.id(), b.id())); + lines.push(format!( + "fieldChip.AssertNeExtension({}, {})", + a.id(), + b.id() + )); } DslIR::AssertEqVI(a, b) => { lines.push(format!( @@ -509,28 +520,28 @@ impl GnarkBackend { } DslIR::AssertEqFI(a, b) => { lines.push(format!( - "fieldChip.AssertEq({}, types.NewFelt({}))", + "fieldChip.AssertEq({}, babybear.NewVariable({}))", a.id(), b )); } DslIR::AssertNeFI(a, b) => { lines.push(format!( - "fieldChip.AssertNe({}, types.NewFelt({}))", + "fieldChip.AssertNe({}, babybear.NewVariable({}))", a.id(), b )); } DslIR::AssertEqEI(a, b) => { lines.push(format!( - "extensionChip.AssertEq({}, types.NewExt({}))", + "fieldChip.AssertEqExtension({}, babybear.NewExtensionVariable({}))", a.id(), b )); } DslIR::AssertNeEI(a, b) => { lines.push(format!( - "extensionChip.AssertNe({}, types.NewExt({}))", + "fieldChip.AssertNeExtension({}, babybear.NewExtensionVariable({}))", a.id(), b )); @@ -541,13 +552,37 @@ impl GnarkBackend { } pub fn compile(&mut self, program: Vec>) -> String { - let lines = self.emit(program); + let operations = self.emit(program); + let initializes = self + .used + .keys() + .map(|id| { + if id.contains("var") { + format!("var {} frontend.Variable", id) + } else if id.contains("felt") { + format!("var {} *babybear.Variable", id) + } else if id.contains("ext") { + format!("var{} *babybear.ExtensionVariable", id) + } else { + panic!("Unknown variable type") + } + }) + .collect::>(); + + let mut lines = Vec::new(); + lines.extend(vec!["".to_string(), "// Variables.".to_string()]); + lines.extend(initializes); + lines.extend(vec!["".to_string(), "// Operations.".to_string()]); + lines.extend(operations); GNARK_TEMPLATE.replace("{{LINES}}", &indent(lines).join("\n")) } } #[cfg(test)] mod tests { + use std::fs::File; + use std::io::Write; + use p3_baby_bear::BabyBear; use p3_field::{extension::BinomialExtensionField, AbstractField}; @@ -613,6 +648,11 @@ mod tests { phantom: PhantomData, }; let result = backend.compile(builder.operations); - println!("{}", result); + + // Write to file. + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let path = format!("{}/src/gnark/lib/main.go", manifest_dir); + let mut file = File::create(path).unwrap(); + file.write_all(result.as_bytes()).unwrap(); } } diff --git a/recursion/compiler/src/lib.rs b/recursion/compiler/src/lib.rs index 32cc234362..1351e5f501 100644 --- a/recursion/compiler/src/lib.rs +++ b/recursion/compiler/src/lib.rs @@ -1,8 +1,8 @@ extern crate alloc; pub mod asm; -pub mod backend; pub mod builder; +pub mod gnark; pub mod heap; pub mod ir; pub mod util;