feat: init

This commit is contained in:
rymnc 2024-03-13 14:01:15 +05:30
commit 33502bd39a
No known key found for this signature in database
GPG Key ID: AAA088D5C68ECD34
9 changed files with 25643 additions and 0 deletions

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# g-rln
gnark implementation of rln-v2. super hacky and unclean.
Need to get kats from zerokit and replace the circuit assertions for y, root, nullifier.
## Usage
```bash
go run main.go
```

34
go.mod Normal file
View File

@ -0,0 +1,34 @@
module github.com/rymnc/g-rln
go 1.22.1
require (
github.com/AlpinYukseloglu/poseidon-gnark v0.0.0-20230513045146-69f5c852ef54
github.com/consensys/gnark v0.9.1
github.com/reilabs/gnark-lean-extractor v1.0.0
)
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/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb // 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/iden3/go-iden3-crypto v0.0.16
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/reilabs/gnark-lean-demo v0.0.0-20230831223820-a3955946e0d5 // 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.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

66
go.sum Normal file
View File

@ -0,0 +1,66 @@
github.com/AlpinYukseloglu/poseidon-gnark v0.0.0-20230513045146-69f5c852ef54 h1:fOzroAaK/5CVGizMvhpBRo1GKPEWasZccNUi+9Y7uQw=
github.com/AlpinYukseloglu/poseidon-gnark v0.0.0-20230513045146-69f5c852ef54/go.mod h1:xn3T1WspWqgbMXkObyFWNnjOUP09SkOK45V9J1vifBk=
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.8.0 h1:0bQ2MyDG4oNjMQpNyL8HjrrUSSL3yYJg0Elzo6LzmcU=
github.com/consensys/gnark v0.8.0/go.mod h1:aKmA7dIiLbTm0OV37xTq0z+Bpe4xER8EhRLi6necrm8=
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/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
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/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/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTorBMEdsk=
github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
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/reilabs/gnark-lean-demo v0.0.0-20230831223820-a3955946e0d5 h1:UxNLMPNbqte3ABu0EDrSH8bo5C2DqdVXf2ChzSZb2ik=
github.com/reilabs/gnark-lean-demo v0.0.0-20230831223820-a3955946e0d5/go.mod h1:n62YzMgj38d/zyjs0I1GGO0Bky9NDX2akGx4W2pmo4A=
github.com/reilabs/gnark-lean-extractor v1.0.0 h1:jLoI4Dh3eAFdhJ3hvUfBmKJgB2NLPGrL7VW+WDRecCQ=
github.com/reilabs/gnark-lean-extractor v1.0.0/go.mod h1:FuCRQ2kdpdImOzmzFUqMoCMQxn+Nol+AmzPDWlBZLA8=
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.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
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.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=

131
main.go Normal file
View File

@ -0,0 +1,131 @@
package main
import (
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
rln "github.com/rymnc/g-rln/rln"
)
func main() {
identityPathIndex := [20]frontend.Variable{
1,
1,
1,
0,
1,
0,
1,
0,
1,
0,
1,
0,
0,
0,
0,
0,
1,
1,
1,
0,
}
rawPathElements := [20]string{
"14082964758224722211945379872337797638951236517417253447686770846170014042825",
"6628418579821163687428454604867534487917867918886059133241840211975892987309",
"12745863228198753394445659605634840709296716381893463421165313830643281758511",
"56118267389743063830320351452083247040583061493621478539311100137113963555",
"3648731943306935051357703221473866306053186513730785325303257057776816073765",
"10548621390442503192989374711060717107954536293658152583621924810330521179016",
"11741160669079729961275351458682156164905457324981803454515784688429276743441",
"17165464309215350864730477596846156251863702878546777829650812432906796008534",
"18947162586829418653666557598416458949428989734998924978331450666032720066913",
"8809427088917589399897132358419395928548406347152047718919154153577297139202",
"6261460226929242970747566981077801929281729646713842579109271945192964422300",
"13871468675790284383809887052382100311103716176061564908030808887079542722597",
"10413964486611723004584705484327518190402370933255450052832412709168190985805",
"3978387560092078849178760154060822400741873818692524912249877867958842934383",
"14014915591348694328771517896715085647041518432952027841088176673715002508448",
"17680675606519345547327984724173632294904524423937145835611954334756161077843",
"17107175244885276119916848057745382329169223109661217238296871427531065458152",
"18326186549441826262593357123467931475982067066825042001499291800252145875109",
"7043961192177345916232559778383741091053414803377017307095275172896944935996",
"2807630271073553218355393059254209097448243975722083008310815929736065268921",
}
pathElements := [20]frontend.Variable{}
// iterate over pathElements and replace with fr.Modulus().SetString("...") for eac, 10h element
for i := 0; i < len(pathElements); i++ {
x, ret := fr.Modulus().SetString(rawPathElements[i], 10)
if ret != true {
panic(ret)
}
pathElements[i] = frontend.Variable(x)
}
cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &rln.RlnCircuit{})
if err != nil {
panic(err)
}
pk, vk, err := groth16.Setup(cs)
if err != nil {
panic(err)
}
x, ret := fr.Modulus().SetString("20645213238265527935869146898028115621427162613172918400241870500502509785943", 10)
if ret != true {
panic(ret)
}
external_nullifier, ret := fr.Modulus().SetString("21074405743803627666274838159589343934394162804826017440941339048886754734203", 10)
if ret != true {
panic(ret)
}
identity_secret, ret := fr.Modulus().SetString("2301650865650889795878889082892690584512243988708213561328369865554257051708", 10)
if ret != true {
panic(ret)
}
assignment := &rln.RlnCircuit{
X: frontend.Variable(x),
ExternalNullifier: frontend.Variable(external_nullifier),
IdentitySecret: frontend.Variable(identity_secret),
MessageId: frontend.Variable(1),
UserMessageLimit: frontend.Variable(100),
PathElements: pathElements,
IdentityPathIndex: identityPathIndex,
Y: frontend.Variable(0),
Root: frontend.Variable(0),
Nullifier: frontend.Variable(0),
}
witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField())
proof, err := groth16.Prove(cs, pk, witness)
if err != nil {
panic(err)
}
raw := &rln.RlnCircuit{
X: frontend.Variable(x),
ExternalNullifier: frontend.Variable(external_nullifier),
Y: frontend.Variable(0),
Root: frontend.Variable(0),
Nullifier: frontend.Variable(0),
}
verifyWitness, err := frontend.NewWitness(raw, ecc.BN254.ScalarField(), frontend.PublicOnly())
if err != nil {
panic(err)
}
err = groth16.Verify(proof, vk, verifyWitness)
if err != nil {
print(err.Error())
}
}

140
rln/poseidon.go Normal file
View File

@ -0,0 +1,140 @@
// forked from https://raw.githubusercontent.com/AlpinYukseloglu/poseidon-gnark/main/circuits/poseidon.go
package rln
import (
"math/big"
"github.com/consensys/gnark/frontend"
)
func Sigma(api frontend.API, in frontend.Variable) frontend.Variable {
return api.Mul(in, in, in, in, in)
}
func Ark(api frontend.API, in []frontend.Variable, c []*big.Int, r int) []frontend.Variable {
for i := range in {
in[i] = api.Add(in[i], c[i+r])
}
return in
}
// Shared logic of multiplication and addition
func multiplyAndAdd(api frontend.API, in []frontend.Variable, factors []*big.Int) frontend.Variable {
result := frontend.Variable(0)
for i, val := range in {
result = api.Add(result, api.Mul(factors[i], val))
}
return result
}
// Helper function to create factors slice for Mix
func createMixFactors(in []frontend.Variable, m [][]*big.Int, index int) []*big.Int {
factors := make([]*big.Int, len(in))
for i := range in {
factors[i] = m[i][index]
}
return factors
}
func Mix(api frontend.API, in []frontend.Variable, m [][]*big.Int) []frontend.Variable {
out := make([]frontend.Variable, len(in))
for i := range in {
out[i] = multiplyAndAdd(api, in, createMixFactors(in, m, i))
}
return out
}
func MixLast(api frontend.API, in []frontend.Variable, m [][]*big.Int, s int) frontend.Variable {
return multiplyAndAdd(api, in, createMixFactors(in, m, s))
}
func MixS(api frontend.API, in []frontend.Variable, s []*big.Int, r int) []frontend.Variable {
out := make([]frontend.Variable, len(in))
for i := range in {
out[0] = api.Add(out[0], api.Mul(s[(len(in)*2-1)*r+i], in[i]))
}
for i := 1; i < len(in); i++ {
out[i] = api.Add(in[i], api.Mul(in[0], s[(len(in)*2-1)*r+len(in)+i-1]))
}
return out
}
func PoseidonEx(api frontend.API, inputs []frontend.Variable, initialState frontend.Variable, nOuts int) []frontend.Variable {
out := make([]frontend.Variable, nOuts)
// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
// And rounded up to nearest integer that divides by t
nRoundsPC := [16]int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68}
t := len(inputs) + 1
nRoundsF := 8
nRoundsP := nRoundsPC[t-2]
c := POSEIDON_C(t)
s := POSEIDON_S(t)
m := POSEIDON_M(t)
p := POSEIDON_P(t)
state := make([]frontend.Variable, t)
for j := 0; j < t; j++ {
if j == 0 {
state[0] = initialState
} else {
state[j] = inputs[j-1]
}
}
state = Ark(api, state, c, 0)
for r := 0; r < nRoundsF/2-1; r++ {
for j := 0; j < t; j++ {
state[j] = Sigma(api, state[j])
}
state = Ark(api, state, c, (r+1)*t)
state = Mix(api, state, m)
}
for j := 0; j < t; j++ {
state[j] = Sigma(api, state[j])
}
state = Ark(api, state, c, nRoundsF/2*t)
state = Mix(api, state, p)
for r := 0; r < nRoundsP; r++ {
state[0] = Sigma(api, state[0])
state[0] = api.Add(state[0], c[(nRoundsF/2+1)*t+r])
newState0 := frontend.Variable(0)
for j := 0; j < len(state); j++ {
mul := api.Mul(s[(t*2-1)*r+j], state[j])
newState0 = api.Add(newState0, mul)
}
for k := 1; k < t; k++ {
state[k] = api.Add(state[k], api.Mul(state[0], s[(t*2-1)*r+t+k-1]))
}
state[0] = newState0
}
for r := 0; r < nRoundsF/2-1; r++ {
for j := 0; j < t; j++ {
state[j] = Sigma(api, state[j])
}
state = Ark(api, state, c, (nRoundsF/2+1)*t+nRoundsP+r*t)
state = Mix(api, state, m)
}
for j := 0; j < t; j++ {
state[j] = Sigma(api, state[j])
}
for i := 0; i < nOuts; i++ {
out[i] = MixLast(api, state, m, i)
}
return out
}
func Poseidon(api frontend.API, inputs []frontend.Variable) frontend.Variable {
out := PoseidonEx(api, inputs, 0, 1)
return out[0]
}

25061
rln/poseidon_constants.go Normal file

File diff suppressed because it is too large Load Diff

83
rln/poseidon_test.go Normal file
View File

@ -0,0 +1,83 @@
package rln
import (
"github.com/consensys/gnark/frontend"
)
// TestPoseidon tests the Gnark Poseidon implementation against Iden3's Go implementation on all the test
// vectors outlined in the original paper's reference repository, which can be found here: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/tree/master/code
//
// The actual test vectors are outlined here: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
// We have included more for the sake of robustness.
// Note that our implementation is focused on the 3-input variant with an x^5 S-box, so not all the test vectors apply.
// func TestPoseidon(t *testing.T) {
// tests := map[string]struct {
// gnarkPoseidonInput [3]frontend.Variable
// referencePoseidonInput []*big.Int
// }{
// "happy path: basic input": {
// gnarkPoseidonInput: [3]frontend.Variable{1, 2, 3},
// referencePoseidonInput: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
// },
// "official test vector: poseidonperm_x5_254_3": {
// gnarkPoseidonInput: [3]frontend.Variable{0, 1, 2},
// referencePoseidonInput: []*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(2)},
// },
// "zero vector": {
// gnarkPoseidonInput: [3]frontend.Variable{0, 0, 0},
// referencePoseidonInput: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)},
// },
// "larger inputs": {
// gnarkPoseidonInput: [3]frontend.Variable{129048, 990217, 2234383333},
// referencePoseidonInput: []*big.Int{big.NewInt(129048), big.NewInt(990217), big.NewInt(2234383333)},
// },
// "decreasing vector inputs": {
// gnarkPoseidonInput: [3]frontend.Variable{10000000, 10000, 100},
// referencePoseidonInput: []*big.Int{big.NewInt(10000000), big.NewInt(10000), big.NewInt(100)},
// },
// }
// for name, testCase := range tests {
// assert := test.NewAssert(t)
// var circuit circuitPoseidon
// // Compute reference hash to test against
// referenceHash, err := poseidon.Hash(testCase.referencePoseidonInput)
// if err != nil {
// t.Fatal(err, "Failed to compute reference poseidon hash for test case: ", name)
// }
// t.Logf("Reference hash: %s", referenceHash.String())
// // Generate poseidon hash using gnark implementation
// assert.ProverSucceeded(&circuit, &circuitPoseidon{
// A: testCase.gnarkPoseidonInput,
// Hash: referenceHash,
// }, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16))
// // Ensure output correctly compiles
// _r1cs, err := frontend.Compile(big.NewInt(1), r1cs.NewBuilder, &circuit)
// if err != nil {
// t.Fatal(err, "Failed to compile computed poseidon hash for test case: ", name)
// }
// // Sanity check and debugging support for internal variables
// internal, secret, public := _r1cs.GetNbVariables()
// t.Logf("Public, secret, internal %v, %v, %v\n", public, secret, internal)
// }
// }
// --- Test Helpers ---
type circuitPoseidon struct {
A [3]frontend.Variable `gnark:",public"`
Hash frontend.Variable `gnark:",public"`
}
func (t *circuitPoseidon) Define(api frontend.API) error {
hash := Poseidon(api, t.A[:])
api.Println(t.Hash)
api.Println(hash)
api.AssertIsEqual(hash, t.Hash)
return nil
}

73
rln/rln.go Normal file
View File

@ -0,0 +1,73 @@
package rln
import (
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/rangecheck"
)
// Circuit defines a simple circuit
// x**3 + x + 5 == y
type RlnCircuit struct {
X frontend.Variable `gnark:"x, public"` // message hash
ExternalNullifier frontend.Variable `gnark:"externalNullifier, public"` // external nullifier
IdentitySecret frontend.Variable `gnark:"identitySecret,secret"` // identity secret
MessageId frontend.Variable `gnark:"messageId,secret"` // message id
UserMessageLimit frontend.Variable `gnark:"userMessageLimit,secret"` // user message limit
PathElements [20]frontend.Variable `gnark:"pathElements,secret"` // path elements
IdentityPathIndex [20]frontend.Variable `gnark:"identityPathIndex,secret"` // identity path index
Y frontend.Variable `gnark:"y,public"`
Root frontend.Variable `gnark:"root, public"`
Nullifier frontend.Variable `gnark:"nullifier, public"`
}
func (circuit RlnCircuit) Define(api frontend.API) error {
var identity_commitment_input [1]frontend.Variable
identity_commitment_input[0] = circuit.IdentitySecret
identity_commitment := Poseidon(api, identity_commitment_input[:])
api.AssertIsEqual(identity_commitment, identity_commitment)
var rate_commitment_input [2]frontend.Variable
rate_commitment_input[0] = identity_commitment
rate_commitment_input[1] = circuit.UserMessageLimit
rate_commitment := Poseidon(api, rate_commitment_input[:])
api.AssertIsEqual(rate_commitment, rate_commitment)
levels := len(circuit.IdentityPathIndex)
hashes := make([]frontend.Variable, levels+1)
hashes[0] = rate_commitment
for i := 0; i < levels; i++ {
api.AssertIsBoolean(circuit.IdentityPathIndex[i])
var left_hash_input [2]frontend.Variable
left_hash_input[0] = hashes[i]
left_hash_input[1] = circuit.PathElements[i]
var right_hash_input [2]frontend.Variable
right_hash_input[0] = circuit.PathElements[i]
right_hash_input[1] = hashes[i]
left_hash := Poseidon(api, left_hash_input[:])
right_hash := Poseidon(api, right_hash_input[:])
hashes[i+1] = api.Select(circuit.IdentityPathIndex[i], right_hash, left_hash)
}
circuit.Root = hashes[levels]
api.AssertIsEqual(circuit.Root, circuit.Root)
rangeChecker := rangecheck.New(api)
rangeChecker.Check(circuit.MessageId, 16)
api.AssertIsLessOrEqual(circuit.MessageId, circuit.UserMessageLimit)
var a1_input [3]frontend.Variable
a1_input[0] = circuit.IdentitySecret
a1_input[1] = circuit.ExternalNullifier
a1_input[2] = circuit.MessageId
a1 := Poseidon(api, a1_input[:])
circuit.Y = api.Mul(api.Add(circuit.IdentitySecret, a1), circuit.X)
api.AssertIsEqual(circuit.Y, circuit.Y)
var nullifier_input [1]frontend.Variable
nullifier_input[0] = a1
circuit.Nullifier = Poseidon(api, nullifier_input[:])
api.AssertIsEqual(circuit.Nullifier, circuit.Nullifier)
return nil
}

44
rln/rln_test.go Normal file
View File

@ -0,0 +1,44 @@
package rln
import (
"testing"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)
func TestRlnCircuit(t *testing.T) {
assert := test.NewAssert(t)
var rlnCircuit RlnCircuit
var identityPathIndex [20]frontend.Variable
for i := 0; i < 20; i++ {
var direction frontend.Variable
if i%2 == 0 {
direction = frontend.Variable(1)
} else {
direction = frontend.Variable(0)
}
identityPathIndex[i] = direction
}
var pathElements [20]frontend.Variable
for i := 0; i < 20; i++ {
pathElements[i] = frontend.Variable(10)
}
assert.ProverSucceeded(&rlnCircuit, &RlnCircuit{
X: frontend.Variable(10),
ExternalNullifier: frontend.Variable(10),
IdentitySecret: frontend.Variable(10),
MessageId: frontend.Variable(10),
UserMessageLimit: frontend.Variable(20),
PathElements: pathElements,
IdentityPathIndex: identityPathIndex,
Y: frontend.Variable(0),
Root: frontend.Variable(0),
Nullifier: frontend.Variable(0),
})
}