diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 23b821cf63..30e442f8d9 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -9,6 +9,7 @@ import ( "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" @@ -18,8 +19,6 @@ import ( bw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark/std/gkrapi/gkr" ) var ( diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 7fe739b152..7140b9b260 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -7,7 +7,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index a782015cfa..7df2419cdc 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -2,8 +2,8 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" - "github.com/consensys/gnark/std/gkrapi/gkr" "{{.FieldPackagePath}}" {{- if .CanUseFFT }} "{{.FieldPackagePath}}/fft" diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 3e3881d15f..de56aec779 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -10,7 +10,7 @@ import ( "sync" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index afeaabf986..c5fe28882d 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -9,7 +9,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" "fmt" "hash" diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 9e5a3868f3..0e5b2f6324 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index b92ac1249d..cbc9c6fad2 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 248a12d8d8..23d7e42dd7 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 5b281fd634..0cb7819a8a 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 82084049d9..8fd80038fd 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 63aaa6ecd7..e81b282d1e 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 058b53cc06..abb2fae6a8 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index f182c9176b..0f32328886 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index ee94fe9d9a..63e8b28893 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index ed418ff1b0..0163c49376 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index a284f14ae9..60c7d78177 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index eda958885d..9f4dc95632 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index e9311a3ea5..9b956db61d 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 14269151b3..166521faa7 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index e03d2aca28..5eb8bf8fd3 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 8074b9621c..7618a6f318 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index ec1067f736..c6ce00d328 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index 20ad407e62..1e9dbd2cf5 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 0bae6258dc..cb58b74847 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "slices" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index ad5197feef..26de3680ca 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 185ca30375..2e8984ad5d 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -24,7 +24,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/stretchr/testify/assert" ) diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 6d14e37dfb..4e058f8be3 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -6,12 +6,6 @@ import ( ) type ( - InputDependency struct { - OutputWire int - OutputInstance int - InputInstance int - } - Wire struct { Gate string Inputs []int @@ -27,13 +21,6 @@ type ( SolveHintID solver.HintID ProveHintID solver.HintID } - - Permutations struct { - SortedInstances []int - SortedWires []int - InstancesPermutation []int - WiresPermutation []int - } ) func (w Wire) IsInput() bool { diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 4c901f04a2..a1b617ae2d 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -10,7 +10,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // Cache for circuits and gates. diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index d313a7bc59..786ff8858a 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -8,8 +8,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "github.com/consensys/gnark/std/polynomial" ) @@ -253,15 +252,6 @@ func StoringToSolvingInfo(info gkrinfo.StoringInfo, gateGetter func(name gkr.Gat // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin -func (a WireAssignment) Permute(p gkrinfo.Permutations) { - utils.Permute(a, p.WiresPermutation) - for i := range a { - if a[i] != nil { - utils.Permute(a[i], p.InstancesPermutation) - } - } -} - func (a WireAssignment) NbInstances() int { for _, aW := range a { if aW != nil { diff --git a/internal/gkr/small_rational/gate_testing.go b/internal/gkr/small_rational/gate_testing.go index 93c4ca4191..22fb75afac 100644 --- a/internal/gkr/small_rational/gate_testing.go +++ b/internal/gkr/small_rational/gate_testing.go @@ -10,13 +10,13 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" "errors" "slices" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index cdf62359f2..e63f98bd02 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/utils/algo_utils.go b/internal/utils/algo_utils.go index 4bee19443e..38d00b1c45 100644 --- a/internal/utils/algo_utils.go +++ b/internal/utils/algo_utils.go @@ -4,26 +4,6 @@ import "github.com/bits-and-blooms/bitset" // this package provides some generic (in both senses of the word) algorithmic conveniences. -// Permute operates in-place but is not thread-safe; it uses the permutation for scratching -// permutation[i] signifies which index slice[i] is going to -func Permute[T any](slice []T, permutation []int) { - var cached T - for next := 0; next < len(permutation); next++ { - - cached = slice[next] - j := permutation[next] - permutation[next] = ^j - for j >= 0 { - cached, slice[j] = slice[j], cached - j, permutation[j] = permutation[j], ^permutation[j] - } - permutation[next] = ^permutation[next] - } - for i := range permutation { - permutation[i] = ^permutation[i] - } -} - // Map returns [f(in[0]), f(in[1]), ..., f(in[len(in)-1])] func Map[T, S any](in []T, f func(T) S) []S { out := make([]S, len(in)) @@ -33,15 +13,13 @@ func Map[T, S any](in []T, f func(T) S) []S { return out } -// TODO: Move this to gnark-crypto and use it for gkr there as well - // TopologicalSort takes a list of lists of dependencies and proposes a sorting of the lists in order of dependence. Such that for any wire, any one it depends on // occurs before it. It tries to stick to the input order as much as possible. An already sorted list will remain unchanged. // As a bonus, it returns for each list its "unique" outputs. That is, a list of its outputs with no duplicates. -// Worst-case inefficient O(n^2), but that probably won't matter since the circuits are small. +// Worst-case inefficient O(n²), but that probably won't matter since the circuits are small. // Furthermore, it is efficient with already-close-to-sorted lists, which are the expected input. // If performance was bad, consider using a heap for finding the value "leastReady". -// WARNING: Due to the current implementation of intSet, it is ALWAYS O(n^2). +// WARNING: Due to the current implementation of intSet, it is ALWAYS O(n²). func TopologicalSort(inputs [][]int) (sorted []int, uniqueOutputs [][]int) { data := newTopSortData(inputs) sorted = make([]int, len(inputs)) diff --git a/internal/utils/algo_utils_test.go b/internal/utils/algo_utils_test.go index 91ef0df2bb..19d3fd5b85 100644 --- a/internal/utils/algo_utils_test.go +++ b/internal/utils/algo_utils_test.go @@ -57,14 +57,3 @@ func TestTopSortWide(t *testing.T) { testTopSort(t, inputs, expectedSorted, expectedNbUniqueOut) } - -func TestPermute(t *testing.T) { - list := []int{34, 65, 23, 2, 5} - permutation := []int{2, 0, 1, 4, 3} - permutationCopy := make([]int, len(permutation)) - copy(permutationCopy, permutation) - - Permute(list, permutation) - assert.Equal(t, []int{65, 23, 34, 5, 2}, list) - assert.Equal(t, permutationCopy, permutation) -} diff --git a/std/gkrapi/v1/api.go b/std/gkrapi/v1/api.go new file mode 100644 index 0000000000..731039f14c --- /dev/null +++ b/std/gkrapi/v1/api.go @@ -0,0 +1,214 @@ +package gkrapi + +import ( + "cmp" + "errors" + "math/bits" + "slices" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/utils" + "github.com/consensys/gnark/std/gkrapi/v2" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" +) + +type API struct { + assignments gkrtypes.WireAssignment + api gkrapi.API + dependencies []inputDependency +} + +type inputDependency struct { + outputWire int + outputInstance int + inputWire int + inputInstance int +} + +func (api *API) NamedGate(gate gkr.GateName, in ...gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.NamedGate(gate, in...) +} + +func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.Gate(gate, in...) +} + +func (api *API) namedGate2PlusIn(gate gkr.GateName, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { + inCombined := make([]gkr.Variable, 2+len(in)) + inCombined[0] = in1 + inCombined[1] = in2 + for i := range in { + inCombined[i+2] = in[i] + } + return api.NamedGate(gate, inCombined...) +} + +func (api *API) Add(i1, i2 gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.Add(i1, i2) +} + +func (api *API) Neg(i1 gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.Neg(i1) +} + +func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.Sub(i1, i2) +} + +func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { + api.assignments = append(api.assignments, nil) + return api.api.Mul(i1, i2) +} + +type solution struct { + assignments gkrtypes.WireAssignment + hashName string + initialChallenges []frontend.Variable +} + +type Solution struct { + *solution +} + +func (api *API) nbInstances() int { + if len(api.assignments) == 0 { + return -1 + } + return api.assignments.NbInstances() +} + +// New creates a new GKR API +// Deprecated: Use [github.com/consensys/gnark/std/gkrapi/v2.New] instead +func New() *API { + return &API{} +} + +// log2 returns -1 if x is not a power of 2 +func log2(x uint) int { + if bits.OnesCount(x) != 1 { + return -1 + } + return bits.TrailingZeros(x) +} + +// Series like in an electric circuit, binds an input of an instance to an output of another +func (api *API) Series(input, output gkr.Variable, inputInstance, outputInstance int) *API { + if api.assignments[input][inputInstance] != nil { + panic("dependency attempting to override explicit value assignment") + } + + api.dependencies = + append(api.dependencies, inputDependency{ + outputWire: int(output), + inputWire: int(input), + outputInstance: outputInstance, + inputInstance: inputInstance, + }) + return api +} + +// Import creates a new input variable, whose values across all instances are given by assignment. +// If the value in an instance depends on an output of another instance, leave the corresponding index in assignment nil and use Series to specify the dependency. +func (api *API) Import(assignment []frontend.Variable) (gkr.Variable, error) { + nbInstances := len(assignment) + logNbInstances := log2(uint(nbInstances)) + if logNbInstances == -1 { + return -1, errors.New("number of assignments must be a power of 2") + } + + if currentNbInstances := api.nbInstances(); currentNbInstances != -1 && currentNbInstances != nbInstances { + return -1, errors.New("number of assignments must be consistent across all variables") + } + api.assignments = append(api.assignments, assignment) + return api.api.NewInput(), nil +} + +// Solve finalizes the GKR circuit and returns the output variables in the order created. +func (api *API) Solve(parentApi frontend.API) (Solution, error) { + res := Solution{&solution{ + assignments: api.assignments, + }} + + // 1. compile circuit + circuit := api.api.Compile( + parentApi, + gkrapi.WithHashNameProvider(func() string { return res.hashName }), + gkrapi.WithInitialChallenge(func() []frontend.Variable { return res.initialChallenges }), + ) + + // 2. sort dependencies so that they can be added in order (for j > i, instance i must not depend on instance j) + dependenciesNoWire := make([][]int, api.nbInstances()) + dependencies := make([][]inputDependency, api.nbInstances()) + for _, dep := range api.dependencies { + dependenciesNoWire[dep.inputInstance] = append(dependenciesNoWire[dep.inputInstance], dep.outputInstance) + dependencies[dep.inputInstance] = append(dependencies[dep.inputInstance], dep) + } + for i := range dependencies { + slices.SortFunc(dependencies[i], func(a, b inputDependency) int { + return cmp.Compare(a.inputWire, b.inputWire) + }) + } + v2ToV1, _ := utils.TopologicalSort(dependenciesNoWire) + + // 3. add instances and sort outputs into the original order + isInput := make([]bool, len(res.assignments)) + for i := range res.assignments { + if res.assignments[i] == nil { + // Note: This is rather inefficient at compile time. Intermediate wires do not need + // explicit assignments. + res.assignments[i] = make([]frontend.Variable, api.nbInstances()) + } else { + isInput[i] = true + } + } + + ins := make(map[gkr.Variable]frontend.Variable) + for _, v1Index := range v2ToV1 { + for wI, assignment := range res.assignments { + if !isInput[wI] { + continue + } + ins[gkr.Variable(wI)] = assignment[v1Index] + if assignment[v1Index] == nil { // dependency + dep := dependencies[v1Index][0] + dependencies[v1Index] = dependencies[v1Index][1:] + + if dep.inputInstance != v1Index || dep.inputWire != wI { + return Solution{nil}, errors.New("unexpected dependency") + } + + ins[gkr.Variable(wI)] = res.assignments[dep.outputWire][dep.outputInstance] + } + } + + outs, err := circuit.AddInstance(ins) + for wI, v := range outs { + res.assignments[wI][v1Index] = v + } + if err != nil { + return Solution{nil}, err + } + + } + + return res, nil +} + +// Export returns the values of an output variable across all instances. +func (s Solution) Export(v gkr.Variable) []frontend.Variable { + return s.assignments[v] +} + +// Verify encodes the verification circuitry for the GKR circuit. +func (s Solution) Verify(hashName string, initialChallenges ...frontend.Variable) error { + s.hashName = hashName + s.initialChallenges = initialChallenges + + return nil +} diff --git a/std/gkrapi/v1/api_test.go b/std/gkrapi/v1/api_test.go new file mode 100644 index 0000000000..54341f0fc1 --- /dev/null +++ b/std/gkrapi/v1/api_test.go @@ -0,0 +1,526 @@ +package gkrapi + +import ( + "fmt" + "hash" + "math/big" + "math/rand" + "slices" + "strconv" + "strings" + "testing" + "time" + + "github.com/consensys/gnark" + "github.com/consensys/gnark-crypto/ecc" + gcHash "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" + stdHash "github.com/consensys/gnark/std/hash" + "github.com/consensys/gnark/test" + "github.com/stretchr/testify/require" +) + +// compressThreshold --> if linear expressions are larger than this, the frontend will introduce +// intermediate constraints. The lower this number is, the faster compile time should be (to a point) +// but resulting circuit will have more constraints (slower proving time). +const compressThreshold = 1000 + +type doubleNoDependencyCircuit struct { + X []frontend.Variable + hashName string +} + +func (c *doubleNoDependencyCircuit) Define(api frontend.API) error { + gkrApi := New() + var x gkr.Variable + var err error + if x, err = gkrApi.Import(c.X); err != nil { + return err + } + z := gkrApi.Add(x, x) + var solution Solution + if solution, err = gkrApi.Solve(api); err != nil { + return err + } + Z := solution.Export(z) + + for i := range Z { + api.AssertIsEqual(Z[i], api.Mul(2, c.X[i])) + } + + return solution.Verify(c.hashName) +} + +func TestDoubleNoDependencyCircuit(t *testing.T) { + assert := test.NewAssert(t) + + xValuess := [][]frontend.Variable{ + {1, 1}, + {1, 2}, + } + + hashes := []string{"-1", "-20"} + + for i, xValues := range xValuess { + for _, hashName := range hashes { + assignment := doubleNoDependencyCircuit{X: xValues} + circuit := doubleNoDependencyCircuit{X: make([]frontend.Variable, len(xValues)), hashName: hashName} + assert.Run(func(assert *test.Assert) { + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) + }, fmt.Sprintf("xValue=%d/hash=%s", i, hashName)) + } + } +} + +type sqNoDependencyCircuit struct { + X []frontend.Variable + hashName string +} + +func (c *sqNoDependencyCircuit) Define(api frontend.API) error { + gkrApi := New() + var x gkr.Variable + var err error + if x, err = gkrApi.Import(c.X); err != nil { + return err + } + z := gkrApi.Mul(x, x) + var solution Solution + if solution, err = gkrApi.Solve(api); err != nil { + return err + } + Z := solution.Export(z) + + for i := range Z { + api.AssertIsEqual(Z[i], api.Mul(c.X[i], c.X[i])) + } + + return solution.Verify(c.hashName) +} + +func TestSqNoDependencyCircuit(t *testing.T) { + assert := test.NewAssert(t) + + xValuess := [][]frontend.Variable{ + {1, 1}, + {1, 2}, + } + + hashes := []string{"-1", "-20"} + + for i, xValues := range xValuess { + for _, hashName := range hashes { + assignment := sqNoDependencyCircuit{X: xValues} + circuit := sqNoDependencyCircuit{X: make([]frontend.Variable, len(xValues)), hashName: hashName} + assert.Run(func(assert *test.Assert) { + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) + }, fmt.Sprintf("xValues=%d/hash=%s", i, hashName)) + } + } +} + +type mulNoDependencyCircuit struct { + X, Y []frontend.Variable + hashName string +} + +func (c *mulNoDependencyCircuit) Define(api frontend.API) error { + gkrApi := New() + var x, y gkr.Variable + var err error + if x, err = gkrApi.Import(c.X); err != nil { + return err + } + if y, err = gkrApi.Import(c.Y); err != nil { + return err + } + + z := gkrApi.Mul(x, y) + var solution Solution + if solution, err = gkrApi.Solve(api); err != nil { + return err + } + Z := solution.Export(z) + + for i := range c.X { + api.AssertIsEqual(Z[i], api.Mul(c.X[i], c.Y[i])) + } + + return solution.Verify(c.hashName) +} + +func TestMulNoDependency(t *testing.T) { + assert := test.NewAssert(t) + xValuess := [][]frontend.Variable{ + {1, 2}, + } + yValuess := [][]frontend.Variable{ + {0, 3}, + } + + hashes := []string{"-1", "-20"} + + for i := range xValuess { + for _, hashName := range hashes { + + assignment := mulNoDependencyCircuit{ + X: xValuess[i], + Y: yValuess[i], + } + circuit := mulNoDependencyCircuit{ + X: make([]frontend.Variable, len(xValuess[i])), + Y: make([]frontend.Variable, len(yValuess[i])), + hashName: hashName, + } + assert.Run(func(assert *test.Assert) { + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) + }, fmt.Sprintf("xValues=%d/hash=%s", i, hashName)) + } + } +} + +type mulWithDependencyCircuit struct { + XLast frontend.Variable + Y []frontend.Variable + hashName string +} + +func (c *mulWithDependencyCircuit) Define(api frontend.API) error { + gkrApi := New() + var x, y gkr.Variable + var err error + + X := make([]frontend.Variable, len(c.Y)) + X[len(c.Y)-1] = c.XLast + if x, err = gkrApi.Import(X); err != nil { + return err + } + if y, err = gkrApi.Import(c.Y); err != nil { + return err + } + + z := gkrApi.Mul(x, y) + + for i := len(X) - 1; i > 0; i-- { + gkrApi.Series(x, z, i-1, i) + } + + var solution Solution + if solution, err = gkrApi.Solve(api); err != nil { + return err + } + X = solution.Export(x) + Y := solution.Export(y) + Z := solution.Export(z) + + lastI := len(X) - 1 + api.AssertIsEqual(Z[lastI], api.Mul(c.XLast, Y[lastI])) + for i := 0; i < lastI; i++ { + api.AssertIsEqual(Z[i], api.Mul(Z[i+1], Y[i])) + } + return solution.Verify(c.hashName) +} + +func TestSolveMulWithDependency(t *testing.T) { + assert := test.NewAssert(t) + assignment := mulWithDependencyCircuit{ + XLast: 1, + Y: []frontend.Variable{3, 2}, + } + circuit := mulWithDependencyCircuit{Y: make([]frontend.Variable, len(assignment.Y)), hashName: "-20"} + assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) +} + +func benchCompile(b *testing.B, circuit frontend.Circuit) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, circuit, frontend.WithCompressThreshold(compressThreshold)) + require.NoError(b, err) + } +} + +func benchProof(b *testing.B, circuit, assignment frontend.Circuit) { + fmt.Println("compiling...") + start := time.Now().UnixMicro() + cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, circuit, frontend.WithCompressThreshold(compressThreshold)) + require.NoError(b, err) + fmt.Println("compiled in", time.Now().UnixMicro()-start, "μs") + fullWitness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + require.NoError(b, err) + //publicWitness := fullWitness.Public() + fmt.Println("setting up...") + pk, _, err := groth16.Setup(cs) + require.NoError(b, err) + + fmt.Println("solving and proving...") + b.ResetTimer() + + for i := 0; i < b.N; i++ { + id := rand.Uint32() % 256 //#nosec G404 -- This is a false positive + start = time.Now().UnixMicro() + fmt.Println("groth16 proving", id) + _, err = groth16.Prove(cs, pk, fullWitness) + require.NoError(b, err) + fmt.Println("groth16 proved", id, "in", time.Now().UnixMicro()-start, "μs") + + fmt.Println("mimc total calls: fr=", mimcFrTotalCalls, ", snark=", mimcSnarkTotalCalls) + } +} + +func init() { + registerMiMCGate() +} + +func registerMiMCGate() { + // register mimc gate + _, err := gkrgates.Register(func(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + mimcSnarkTotalCalls++ + + if len(input) != 2 { + panic("mimc has fan-in 2") + } + sum := api.Add(input[0], input[1] /*, m.Ark*/) + + sumCubed := api.Mul(sum, sum, sum) // sum^3 + return api.Mul(sumCubed, sumCubed, sum) + }, 2, gkrgates.WithDegree(7), gkrgates.WithName("MIMC")) + panicIfError(err) +} + +type constPseudoHash int + +func (c constPseudoHash) Sum() frontend.Variable { + return int(c) +} + +func (c constPseudoHash) Write(...frontend.Variable) {} + +func (c constPseudoHash) Reset() {} + +var mimcFrTotalCalls = 0 + +type mimcNoGkrCircuit struct { + X []frontend.Variable + Y []frontend.Variable + mimcDepth int +} + +func (c *mimcNoGkrCircuit) Define(api frontend.API) error { + Z := make([]frontend.Variable, len(c.X)) + zSum := frontend.Variable(0) + for i := range Z { + Z[i] = c.Y[i] + for j := 0; j < c.mimcDepth; j++ { + Z[i] = MiMCCipherGate{Ark: 0}.Evaluate(api, c.X[i], Z[i]) + } + zSum = api.Add(zSum, Z[i]) + } + api.AssertIsDifferent(zSum, 0) + return nil +} + +func BenchmarkMiMCMerkleTreeNoGkrNoDep(b *testing.B) { + nbInstances := 1 << 18 + X := make([]frontend.Variable, nbInstances) + Y := make([]frontend.Variable, nbInstances) + for i := range X { + X[i] = i + Y[i] = -2*i + 1 + } + assignment := mimcNoGkrCircuit{ + X: X, + Y: Y, + } + circuit := mimcNoGkrCircuit{ + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + } + + benchProof(b, &circuit, &assignment) +} + +type mimcNoDepCircuit struct { + X []frontend.Variable + Y []frontend.Variable + mimcDepth int + hashName string +} + +func (c *mimcNoDepCircuit) Define(api frontend.API) error { + _gkr := New() + x, err := _gkr.Import(c.X) + if err != nil { + return err + } + var ( + y gkr.Variable + solution Solution + ) + if y, err = _gkr.Import(c.Y); err != nil { + return err + } + + z := _gkr.NamedGate("MIMC", x, y) + + if solution, err = _gkr.Solve(api); err != nil { + return err + } + Z := solution.Export(z) + return solution.Verify(c.hashName, Z...) +} + +func mimcNoDepCircuits(mimcDepth, nbInstances int, hashName string) (circuit, assignment frontend.Circuit) { + X := make([]frontend.Variable, nbInstances) + Y := make([]frontend.Variable, nbInstances) + for i := range X { + X[i] = i + Y[i] = -2*i + 1 + } + assignment = &mimcNoDepCircuit{ + X: X, + Y: Y, + } + circuit = &mimcNoDepCircuit{ + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + mimcDepth: mimcDepth, + hashName: hashName, + } + return +} + +func BenchmarkMiMCNoDepSolve(b *testing.B) { + //defer profile.Start().Stop() + circuit, assignment := mimcNoDepCircuits(1, 1<<9, "-20") + benchProof(b, circuit, assignment) +} + +func BenchmarkMiMCFullDepthNoDepSolve(b *testing.B) { + circuit, assignment := mimcNoDepCircuits(91, 1<<19, "-20") + benchProof(b, circuit, assignment) +} + +func BenchmarkMiMCFullDepthNoDepCompile(b *testing.B) { + circuit, _ := mimcNoDepCircuits(91, 1<<17, "-20") + benchCompile(b, circuit) +} + +func BenchmarkMiMCNoGkrFullDepthSolve(b *testing.B) { + circuit, assignment := mimcNoGkrCircuits(91, 1<<19) + benchProof(b, circuit, assignment) +} + +func TestMiMCNoDepSolve(t *testing.T) { + assert := test.NewAssert(t) + + for _, depth := range []int{1, 2, 100} { + for _, nbInstances := range []int{1 << 1, 1 << 2, 1 << 5} { + circuit, assignment := mimcNoDepCircuits(depth, nbInstances, "-20") + assert.Run(func(assert *test.Assert) { + assert.CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(ecc.BN254)) + }, fmt.Sprintf("depth=%d, nbInstances=%d", depth, nbInstances)) + } + } +} + +func TestMiMCShallowNoDepSolveWithMiMCHash(t *testing.T) { + assert := test.NewAssert(t) + circuit, assignment := mimcNoDepCircuits(5, 1<<3, "MIMC") + assert.CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(ecc.BN254)) +} + +func mimcNoGkrCircuits(mimcDepth, nbInstances int) (circuit, assignment frontend.Circuit) { + X := make([]frontend.Variable, nbInstances) + Y := make([]frontend.Variable, nbInstances) + for i := range X { + X[i] = i + Y[i] = -2*i + 1 + } + assignment = &mimcNoGkrCircuit{ + X: X, + Y: Y, + } + circuit = &mimcNoGkrCircuit{ + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + mimcDepth: mimcDepth, + } + return +} + +func panicIfError(err error) { + if err != nil { + panic(err) + } +} + +var mimcSnarkTotalCalls = 0 + +type MiMCCipherGate struct { + Ark frontend.Variable +} + +func (m MiMCCipherGate) Evaluate(api frontend.API, input ...frontend.Variable) frontend.Variable { + mimcSnarkTotalCalls++ + + if len(input) != 2 { + panic("mimc has fan-in 2") + } + sum := api.Add(input[0], input[1], m.Ark) + + sumCubed := api.Mul(sum, sum, sum) // sum^3 + return api.Mul(sumCubed, sumCubed, sum) +} + +func (m MiMCCipherGate) Degree() int { + return 7 +} + +type constBytesPseudoHash []byte + +func (c constBytesPseudoHash) Write(p []byte) (n int, err error) { + return len(p), nil +} + +func (c constBytesPseudoHash) Sum([]byte) []byte { + return slices.Clone(c) +} + +func (c constBytesPseudoHash) Reset() { +} + +func (c constBytesPseudoHash) Size() int { + return len(c) +} + +func (c constBytesPseudoHash) BlockSize() int { + return len(c) +} + +func newConstBytesPseudoHash(c int64, mod *big.Int) constBytesPseudoHash { + i := big.NewInt(c) + i.Mod(i, mod) + b := make([]byte, len(mod.Bytes())) + i.FillBytes(b) + return b +} + +func init() { + // register custom (constant) "hash" functions + for _, v := range []int64{-1, -20} { + name := strconv.Itoa(int(v)) + stdHash.RegisterCustomHash(name, func(api frontend.API) (stdHash.FieldHasher, error) { + return constPseudoHash(v), nil + }) + for _, curve := range gnark.Curves() { + h := newConstBytesPseudoHash(v, curve.ScalarField()) + gcHash.RegisterCustomHash(name+"_"+strings.ToUpper(curve.String()), func() hash.Hash { + return h + }) + } + } +} diff --git a/std/gkrapi/v1/example_test.go b/std/gkrapi/v1/example_test.go new file mode 100644 index 0000000000..af57068e46 --- /dev/null +++ b/std/gkrapi/v1/example_test.go @@ -0,0 +1,227 @@ +package gkrapi_test + +import ( + "encoding/binary" + "errors" + + "github.com/consensys/gnark-crypto/ecc" + bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/gkrapi/v1" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" + _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to register them + "github.com/consensys/gnark/test" +) + +func Example() { + // This example computes the double of multiple BLS12-377 G1 points, which can be computed natively over BW6-761. + // This means that the imported fr and fp packages are the same, being from BW6-761 and BLS12-377 respectively. TODO @Tabaie delete if no longer have fp imported + // It is based on the function DoubleAssign() of type G1Jac in gnark-crypto v0.17.0. + // github.com/consensys/gnark-crypto/ecc/bls12-377 + const fsHashName = "MIMC" + + // register the gates: Doing so is not needed here because + // the proof is being computed in the same session as the + // SNARK circuit being compiled. + // But in production applications it would be necessary. + + assertTrueAndNoError(gkrgates.Register(squareGate, 1)) + assertTrueAndNoError(gkrgates.Register(sGate, 4)) + assertTrueAndNoError(gkrgates.Register(zGate, 4)) + assertTrueAndNoError(gkrgates.Register(xGate, 2)) + assertTrueAndNoError(gkrgates.Register(yGate, 4)) + + const nbInstances = 2 + // create instances + assignment := exampleCircuit{ + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + Z: make([]frontend.Variable, nbInstances), + XOut: make([]frontend.Variable, nbInstances), + YOut: make([]frontend.Variable, nbInstances), + ZOut: make([]frontend.Variable, nbInstances), + } + + for i := range nbInstances { + // create a "random" point + var b [8]byte + binary.BigEndian.PutUint64(b[:], uint64(i)) + a, err := bls12377.HashToG1(b[:], nil) + assertNoError(err) + var p bls12377.G1Jac + p.FromAffine(&a) + + assignment.X[i] = p.X + assignment.Y[i] = p.Y + assignment.Z[i] = p.Z + + p.DoubleAssign() + assignment.XOut[i] = p.X + assignment.YOut[i] = p.Y + assignment.ZOut[i] = p.Z + } + + circuit := exampleCircuit{ + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + Z: make([]frontend.Variable, nbInstances), + XOut: make([]frontend.Variable, nbInstances), + YOut: make([]frontend.Variable, nbInstances), + ZOut: make([]frontend.Variable, nbInstances), + fsHashName: fsHashName, + } + + assertNoError(test.IsSolved(&circuit, &assignment, ecc.BW6_761.ScalarField())) + + // Output: +} + +type exampleCircuit struct { + X, Y, Z []frontend.Variable // Jacobian coordinates for each point (input) + XOut, YOut, ZOut []frontend.Variable // Jacobian coordinates for the double of each point (expected output) + fsHashName string // name of the hash function used for Fiat-Shamir in the GKR verifier +} + +func (c *exampleCircuit) Define(api frontend.API) error { + if len(c.X) != len(c.Y) || len(c.X) != len(c.Z) || len(c.X) != len(c.XOut) || len(c.X) != len(c.YOut) || len(c.X) != len(c.ZOut) { + return errors.New("all inputs/outputs must have the same length (i.e. the number of instances)") + } + + gkrApi := gkrapi.New() + + // create GKR circuit variables based on the given assignments + X, err := gkrApi.Import(c.X) + if err != nil { + return err + } + + Y, err := gkrApi.Import(c.Y) + if err != nil { + return err + } + + Z, err := gkrApi.Import(c.Z) + if err != nil { + return err + } + + XX := gkrApi.Gate(squareGate, X) // 405: XX.Square(&p.X) + YY := gkrApi.Gate(squareGate, Y) // 406: YY.Square(&p.Y) + YYYY := gkrApi.Gate(squareGate, YY) // 407: YYYY.Square(&YY) + ZZ := gkrApi.Gate(squareGate, Z) // 408: ZZ.Square(&p.Z) + + S := gkrApi.Gate(sGate, X, YY, XX, YYYY) // 409 - 413 + + // 414: M.Double(&XX).Add(&M, &XX) + // Note (but don't explicitly compute) that M = 3XX + + Z = gkrApi.Gate(zGate, Z, Y, YY, ZZ) // 415 - 418 + X = gkrApi.Gate(xGate, XX, S) // 419-422 + Y = gkrApi.Gate(yGate, S, X, XX, YYYY) // 423 - 426 + + // have to duplicate X for it to be considered an output variable + X = gkrApi.NamedGate(gkr.Identity, X) + + // solve and prove the circuit + solution, err := gkrApi.Solve(api) + if err != nil { + return err + } + + // check the output + + XOut := solution.Export(X) + YOut := solution.Export(Y) + ZOut := solution.Export(Z) + for i := range XOut { + api.AssertIsEqual(XOut[i], c.XOut[i]) + api.AssertIsEqual(YOut[i], c.YOut[i]) + api.AssertIsEqual(ZOut[i], c.ZOut[i]) + } + + challenges := make([]frontend.Variable, 0, len(c.X)*6) + challenges = append(challenges, XOut...) + challenges = append(challenges, YOut...) + challenges = append(challenges, ZOut...) + challenges = append(challenges, c.X...) + challenges = append(challenges, c.Y...) + challenges = append(challenges, c.Z...) + + challenge, err := api.(frontend.Committer).Commit(challenges...) + if err != nil { + return err + } + + // verify the proof + return solution.Verify(c.fsHashName, challenge) +} + +// custom gates + +// squareGate x -> x² +func squareGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + return api.Mul(input[0], input[0]) +} + +// sGate combines the operations that define the first value assigned to variable S. +// input = [X, YY, XX, YYYY]. +// S = 2 * [(X + YY)² - XX - YYYY]. +func sGate(api gkr.GateAPI, input ...frontend.Variable) (S frontend.Variable) { + S = api.Add(input[0], input[1]) // 409: S.Add(&p.X, &YY) + S = api.Mul(S, S) // 410: S.Square(&S). + S = api.Sub(S, input[2], input[3]) // 411: Sub(&S, &XX). + // 412: Sub(&S, &YYYY). + return api.Add(S, S) // 413: Double(&S) +} + +// zGate combines the operations that define the assignment to p.Z. +// input = [p.Z, p.Y, YY, ZZ]. +// p.Z = (p.Z + p.Y)² - YY - ZZ. +func zGate(api gkr.GateAPI, input ...frontend.Variable) (Z frontend.Variable) { + Z = api.Add(input[0], input[1]) // 415: p.Z.Add(&p.Z, &p.Y). + Z = api.Mul(Z, Z) // 416: p.Z.Square(&p.Z). + Z = api.Sub(Z, input[2], input[3]) // 417: Sub(&p.Z, &YY). + // 418: Sub(&p.Z, &ZZ) + return +} + +// xGate combines the operations that define the assignment to p.X. +// input = [XX, S]. +// p.X = 9XX² - 2S. +func xGate(api gkr.GateAPI, input ...frontend.Variable) (X frontend.Variable) { + M := api.Mul(input[0], 3) // 414: M.Double(&XX).Add(&M, &XX) + T := api.Mul(M, M) // 419: T.Square(&M) + X = api.Sub(T, api.Mul(input[1], 2)) // 420: p.X = T + // 421: T.Double(&S) + // 422: p.X.Sub(&p.X, &T) + return +} + +// yGate combines the operations that define the assignment to p.Y. +// input = [S, p.X, XX, YYYY]. +// p.Y = (S - p.X) * 3 * XX - 8 * YYYY. +func yGate(api gkr.GateAPI, input ...frontend.Variable) (Y frontend.Variable) { + Y = api.Sub(input[0], input[1]) // 423: p.Y.Sub(&S, &p.X). + Y = api.Mul(Y, input[2], 3) // 414: M.Double(&XX).Add(&M, &XX) + // 424:Mul(&p.Y, &M) + Y = api.Sub(Y, api.Mul(input[3], 8)) // 425: YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY) + // 426: p.Y.Sub(&p.Y, &YYYY) + + return +} + +func assertNoError(err error) { + if err != nil { + panic(err) + } +} + +func assertTrueAndNoError(b bool, err error) { + if err != nil { + panic(err) + } + if !b { + panic("assertion failed") + } +} diff --git a/std/gkrapi/api.go b/std/gkrapi/v2/api.go similarity index 97% rename from std/gkrapi/api.go rename to std/gkrapi/v2/api.go index ae3c2b7954..6ff1eeb460 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/v2/api.go @@ -5,7 +5,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" ) type API struct { diff --git a/std/gkrapi/api_test.go b/std/gkrapi/v2/api_test.go similarity index 97% rename from std/gkrapi/api_test.go rename to std/gkrapi/v2/api_test.go index 1f3d187dae..3cc39ce3da 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/v2/api_test.go @@ -18,7 +18,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/frontend/cs/scs" - "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/gkrapi/v2/gkr" stdHash "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/test" "github.com/stretchr/testify/assert" @@ -40,7 +40,7 @@ func (c *doubleNoDependencyCircuit) Define(api frontend.API) error { x := gkrApi.NewInput() z := gkrApi.Add(x, x) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit := gkrApi.Compile(api, WithHashName(c.hashName)) instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -86,7 +86,7 @@ func (c *sqNoDependencyCircuit) Define(api frontend.API) error { x := gkrApi.NewInput() z := gkrApi.Mul(x, x) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit := gkrApi.Compile(api, WithHashName(c.hashName)) instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -132,7 +132,7 @@ func (c *mulNoDependencyCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Mul(x, y) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit := gkrApi.Compile(api, WithHashName(c.hashName)) instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -190,7 +190,7 @@ func (c *mulWithDependencyCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Mul(x, y) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit := gkrApi.Compile(api, WithHashName(c.hashName)) state := c.XFirst instanceIn := make(map[gkr.Variable]frontend.Variable) @@ -292,7 +292,7 @@ func (c *benchMiMCMerkleTreeCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Gate(mimcGate, x, y) - gkrCircuit := gkrApi.Compile(api, "-20") + gkrCircuit := gkrApi.Compile(api, WithHashName("-20")) // prepare input curLayer := make([]frontend.Variable, 1<