Compare commits

..

20 Commits

Author SHA1 Message Date
Alvaro Revuelta
a8e8aab76c
Remove hardcoded epoch size (#24) 2024-06-14 12:20:49 +02:00
Alvaro Revuelta
88462cf654
Add InsertRawLeaf (#23)
Co-authored-by: richΛrd <info@richardramos.me>
2024-05-31 07:11:54 +02:00
Alvaro Revuelta
84d12e61d9
Custom witness proof with RLN v2 (#22) 2024-05-30 14:53:54 +02:00
Alvaro Revuelta
54bb48f178
Update to RLN v2 (#21) 2024-05-24 10:17:29 +02:00
Alvaro Revuelta
14960f3aff
Add GenerateRLNProofWithWitness (#19) 2024-01-24 16:31:36 +01:00
Alvaro Revuelta
7e086e8f89
Add GetMerkleProof (#17) 2024-01-16 16:33:45 +01:00
Alvaro Revuelta
06e6fa3fd1
Add CI for tests (#18) 2024-01-16 15:31:15 +01:00
Prem Chaitanya Prathi
fa738c0bdf fix: make rln rate limit spec compliant 2024-01-02 10:52:50 -04:00
Richard Ramos
e0f344a581
chore: update README.md 2023-12-15 08:50:18 -04:00
Richard Ramos
4b71a4fcf1
chore: update README.md with instructions on how to update zerokit 2023-12-15 08:34:52 -04:00
Richard Ramos
d284a3d8f2
chore: add reference to library folders for go mod vendor 2023-09-16 13:32:59 -04:00
Richard Ramos
e0ebce7c29
feat: upgrade to zerokit 0.3.4 2023-09-14 19:40:36 -04:00
Richard Ramos
ca686a02e8
feat: upgrade to zerokit 0.3.2 2023-09-05 17:46:45 -04:00
Richard Ramos
a706089284
chore: use flush_every_ms and change datatype to time.Duration 2023-08-23 11:08:36 -04:00
Richard Ramos
8167006b94
fix: invalid apple commit 2023-08-21 12:37:54 -04:00
Richard Ramos
1ccba817b5
chore: add Bytes32ToBigInt 2023-08-17 18:19:53 -04:00
Richard Ramos
3b5c0bbefb
chore: add BigIntToBytes32 utils function 2023-08-14 07:57:45 -04:00
Richard Ramos
47b8b17401 refactor: only expose tree config 2023-08-11 08:46:33 -04:00
Richard Ramos
fd3fa1222b refactor: remove embedded resource and add depth and mode enums 2023-08-11 08:46:33 -04:00
Richard Ramos
655973b243 chore: allow passing configs when instantiating RLN 2023-08-07 15:54:39 -04:00
20 changed files with 1285 additions and 669 deletions

20
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,20 @@
on: [push, pull_request]
name: Tests
jobs:
test:
strategy:
matrix:
go-version: [1.19.x]
# Disabled: windows-latest
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests
run: |
go test ./... -v

4
.gitignore vendored
View File

@ -13,3 +13,7 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
# Tree persistence
snap.*
blobs/

View File

@ -1,3 +1,82 @@
# go-zerokit-rln # go-zerokit-rln
Go wrappers for [zerokit's RLN](https://github.com/vacp2p/zerokit) Go wrappers for [zerokit's RLN](https://github.com/vacp2p/zerokit)
### Updating vacp2p/zerokit
To overcome the limit of 500mb github has for repositories, go-zerokit-rln depends on 3 projects:
- https://github.com/waku-org/go-zerokit-rln-apple
- https://github.com/waku-org/go-zerokit-rln-arm
- https://github.com/waku-org/go-zerokit-rln-x86_64
Zerokit must be updated in these 3 repositories. The instructions are the same for each of the architectures,
except for `-apple` which require macos to be executed. You need to have docker and rust installed.
```bash
export GO_RLN_ARCH=x86_64 # Replace this for x86_64, arm or apple
export ZEROKIT_COMMIT=master # Use a commit, branch or tag
git clone https://github.com/waku-org/go-zerokit-rln_${GO_RLN_ARCH}
cd go-zerokit-rln-${GO_RLN_ARCH}
git submodule init
git submodule update --recursive
cd zerokit
git pull
git checkout ${ZEROKIT_COMMIT}
cd ..
make
git add zerokit
git add libs/*/librln.a
git commit -m "chore: bump zerokit"
git push
```
Once you execute the previous commands for each one of the architectures, update go.mod:
```bash
cd /path/to/go-zerokit-rln
go get github.com/waku-org/go-zerokit-rln-apple@latest
go get github.com/waku-org/go-zerokit-rln-arm@latest
go get github.com/waku-org/go-zerokit-rln-x86_64@latest
git checkout master
git add go.mod
git add go.sum
git commit -m "chore: bump zerokit"
git push
```
And later in go-waku, update the go-zerokit-rln dependency with
```
cd /path/to/go-waku
git fetch
git checkout -b `date +"%Y%m%d%H%M%S"-bump-zerokit` origin/master
go get github.com/waku-org/go-zerokit-rln@latest
git add go.mod
git add go.sum
git commit -m "chore: bump go-zerokit-rln"
git push
````
And create a PR
# Adding a new architecture
1. Depending on the architecture/platform you want to add, clone one of these repositories.
- https://github.com/waku-org/go-zerokit-rln-apple
- https://github.com/waku-org/go-zerokit-rln-arm
- https://github.com/waku-org/go-zerokit-rln-x86_64
2. Edit `./scripts/build.sh` to add the new architecture. Refer to this list of [supported targets](https://github.com/cross-rs/cross#supported-targets)
3. Edit `./rln/link.go` to add a **cgo** build tag for the new architecture
4. Commit the changes and then execute
```bash
export GO_RLN_ARCH=x86_64 # Replace this for the platform you just updated: x86_64, arm or apple
cd /path/to/go-zerokit-rln
git checkout master
git pull
go get github.com/waku-org/go-zerokit-rln-${GO_RLN_ARCH}@latest
git add go.mod
git add go.sum
git commit -m "chore: bump go-zerokit-rln-${GO_RLN_ARCH}"
git push
```

17
go.mod
View File

@ -1,18 +1,23 @@
module github.com/waku-org/go-zerokit-rln module github.com/waku-org/go-zerokit-rln
go 1.18 go 1.19
require ( require (
github.com/stretchr/testify v1.7.2 github.com/consensys/gnark-crypto v0.12.1
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230803113401-9a7ef94d120e github.com/sirupsen/logrus v1.9.3
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230801152407-8101ff87ee0a github.com/stretchr/testify v1.8.4
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230801140722-0a4e68d0b8f5 github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240529153423-5df5db48b69f
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240529153432-be2c8ac0a840
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240529153442-f5fb416605f5
golang.org/x/crypto v0.18.0
) )
require ( require (
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

53
go.sum
View File

@ -1,26 +1,53 @@
github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230803113401-9a7ef94d120e h1:Ad0rJod5F1FuYCJ8SUB/bQZsQwirNHQRE0IcaVloxZo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230803113401-9a7ef94d120e/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230801152407-8101ff87ee0a h1:10cre+P76QvnLeyeCVAM8WDbUCri/y5xY3LtwI9Y5DE= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176 h1:ezeAofaW3B6tfqS06FwKAKKXpNkimWnIwKjDU0dDPKE=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230801152407-8101ff87ee0a/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230801140722-0a4e68d0b8f5 h1:GseAHwGMixJ2zlY1kFYr3z1Ts0dREIYbgW4yIji9Ksw= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c h1:/eGH8EAt5/zGfNRBQ0nJMrfZDeXRSJrm8E8uCPlsC3A=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230801140722-0a4e68d0b8f5/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240528140707-ed6b40a98d7b h1:LEa2s1p+Z8SN475dVr3XDmvmGyKzIDKPcAQ+6hTyVwA=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240528140707-ed6b40a98d7b/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240529153423-5df5db48b69f h1:CEBW4vu8I60OakKExZUE7G4oY7Z/glQXxPYedpZ4Sq8=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240529153423-5df5db48b69f/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113 h1:dPwc4LAWLXb4Pssej/NtGA9A0UMQwi+JafQPdnhjRWM=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240523161300-8203361a01d0 h1:IvtkZOcApOkEmHkT/drDmMtY6fdYpF7x4sesWyIURpI=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240523161300-8203361a01d0/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240529153432-be2c8ac0a840 h1:DKub+sG+vfKqwOCaKrthhJA/bP7gTZWxbdrFV86Q5Ms=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240529153432-be2c8ac0a840/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc h1:GUZlr25hXLu/PeASqm8P5dPOyD4CdfvkzyEtXEBLbr8=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240523161247-6f16d12c5a86 h1:PN1WSt3u/DEIn4hX5Oqrm9bm5nf5VBfenfXmbX4mg60=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240523161247-6f16d12c5a86/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240529153442-f5fb416605f5 h1:ZhrzpAjIUZHD6gSKPA8zwHjIys9/GTGN3hPKtwMORSA=
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240529153442-f5fb416605f5/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -5,22 +5,24 @@
package link package link
import r "github.com/waku-org/go-zerokit-rln-apple/rln" import (
r "github.com/waku-org/go-zerokit-rln-apple/rln"
)
type RLNWrapper struct { type RLNWrapper struct {
ffi *r.RLN ffi *r.RLN
} }
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) { func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.NewWithFolder(depth, resourcesFolderPath) rln, err := r.New(uint(depth), config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,10 +85,18 @@ func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
return i.ffi.GetLeaf(index) return i.ffi.GetLeaf(index)
} }
func (i RLNWrapper) GetMerkleProof(index uint) ([]byte, error) {
return i.ffi.GetMerkleProof(index)
}
func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) { func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProof(input) return i.ffi.GenerateRLNProof(input)
} }
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProofWithWitness(input)
}
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) { func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
return i.ffi.VerifyWithRoots(input, roots) return i.ffi.VerifyWithRoots(input, roots)
} }
@ -95,6 +105,10 @@ func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) b
return i.ffi.AtomicOperation(index, leaves, indices) return i.ffi.AtomicOperation(index, leaves, indices)
} }
func (i RLNWrapper) SeqAtomicOperation(leaves []byte, indices []byte) bool {
return i.ffi.SeqAtomicOperation(leaves, indices)
}
func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) { func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) {
return i.ffi.RecoverIDSecret(proof1, proof2) return i.ffi.RecoverIDSecret(proof1, proof2)
} }
@ -106,3 +120,11 @@ func (i RLNWrapper) SetMetadata(metadata []byte) bool {
func (i RLNWrapper) GetMetadata() ([]byte, error) { func (i RLNWrapper) GetMetadata() ([]byte, error) {
return i.ffi.GetMetadata() return i.ffi.GetMetadata()
} }
func (i RLNWrapper) Flush() bool {
return i.ffi.Flush()
}
func (i RLNWrapper) LeavesSet() uint {
return i.ffi.LeavesSet()
}

View File

@ -4,22 +4,24 @@
package link package link
import r "github.com/waku-org/go-zerokit-rln-arm/rln" import (
r "github.com/waku-org/go-zerokit-rln-arm/rln"
)
type RLNWrapper struct { type RLNWrapper struct {
ffi *r.RLN ffi *r.RLN
} }
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) { func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.NewWithFolder(depth, resourcesFolderPath) rln, err := r.New(uint(depth), config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -82,10 +84,18 @@ func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
return i.ffi.GetLeaf(index) return i.ffi.GetLeaf(index)
} }
func (i RLNWrapper) GetMerkleProof(index uint) ([]byte, error) {
return i.ffi.GetMerkleProof(index)
}
func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) { func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProof(input) return i.ffi.GenerateRLNProof(input)
} }
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProofWithWitness(input)
}
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) { func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
return i.ffi.VerifyWithRoots(input, roots) return i.ffi.VerifyWithRoots(input, roots)
} }
@ -94,6 +104,10 @@ func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) b
return i.ffi.AtomicOperation(index, leaves, indices) return i.ffi.AtomicOperation(index, leaves, indices)
} }
func (i RLNWrapper) SeqAtomicOperation(leaves []byte, indices []byte) bool {
return i.ffi.SeqAtomicOperation(leaves, indices)
}
func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) { func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) {
return i.ffi.RecoverIDSecret(proof1, proof2) return i.ffi.RecoverIDSecret(proof1, proof2)
} }
@ -105,3 +119,11 @@ func (i RLNWrapper) SetMetadata(metadata []byte) bool {
func (i RLNWrapper) GetMetadata() ([]byte, error) { func (i RLNWrapper) GetMetadata() ([]byte, error) {
return i.ffi.GetMetadata() return i.ffi.GetMetadata()
} }
func (i RLNWrapper) Flush() bool {
return i.ffi.Flush()
}
func (i RLNWrapper) LeavesSet() uint {
return i.ffi.LeavesSet()
}

View File

@ -5,22 +5,24 @@
package link package link
import r "github.com/waku-org/go-zerokit-rln-x86_64/rln" import (
r "github.com/waku-org/go-zerokit-rln-x86_64/rln"
)
type RLNWrapper struct { type RLNWrapper struct {
ffi *r.RLN ffi *r.RLN
} }
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) { func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.NewWithFolder(depth, resourcesFolderPath) rln, err := r.New(uint(depth), config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,10 +85,18 @@ func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
return i.ffi.GetLeaf(index) return i.ffi.GetLeaf(index)
} }
func (i RLNWrapper) GetMerkleProof(index uint) ([]byte, error) {
return i.ffi.GetMerkleProof(index)
}
func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) { func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProof(input) return i.ffi.GenerateRLNProof(input)
} }
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
return i.ffi.GenerateRLNProofWithWitness(input)
}
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) { func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
return i.ffi.VerifyWithRoots(input, roots) return i.ffi.VerifyWithRoots(input, roots)
} }
@ -95,6 +105,10 @@ func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) b
return i.ffi.AtomicOperation(index, leaves, indices) return i.ffi.AtomicOperation(index, leaves, indices)
} }
func (i RLNWrapper) SeqAtomicOperation(leaves []byte, indices []byte) bool {
return i.ffi.SeqAtomicOperation(leaves, indices)
}
func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) { func (i RLNWrapper) RecoverIDSecret(proof1 []byte, proof2 []byte) ([]byte, error) {
return i.ffi.RecoverIDSecret(proof1, proof2) return i.ffi.RecoverIDSecret(proof1, proof2)
} }
@ -106,3 +120,11 @@ func (i RLNWrapper) SetMetadata(metadata []byte) bool {
func (i RLNWrapper) GetMetadata() ([]byte, error) { func (i RLNWrapper) GetMetadata() ([]byte, error) {
return i.ffi.GetMetadata() return i.ffi.GetMetadata()
} }
func (i RLNWrapper) Flush() bool {
return i.ffi.Flush()
}
func (i RLNWrapper) LeavesSet() uint {
return i.ffi.LeavesSet()
}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
package resources
//go:generate go-bindata -pkg resources -o ./bindata.go ./...

View File

@ -1,119 +0,0 @@
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 6,
"vk_alpha_1": [
"20124996762962216725442980738609010303800849578410091356605067053491763969391",
"9118593021526896828671519912099489027245924097793322973632351264852174143923",
"1"
],
"vk_beta_2": [
[
"4693952934005375501364248788849686435240706020501681709396105298107971354382",
"14346958885444710485362620645446987998958218205939139994511461437152241966681"
],
[
"16851772916911573982706166384196538392731905827088356034885868448550849804972",
"823612331030938060799959717749043047845343400798220427319188951998582076532"
],
[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_delta_2": [
[
"8353516066399360694538747105302262515182301251524941126222712285088022964076",
"9329524012539638256356482961742014315122377605267454801030953882967973561832"
],
[
"16805391589556134376869247619848130874761233086443465978238468412168162326401",
"10111259694977636294287802909665108497237922060047080343914303287629927847739"
],
[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"12608968655665301215455851857466367636344427685631271961542642719683786103711",
"9849575605876329747382930567422916152871921500826003490242628251047652318086"
],
[
"6322029441245076030714726551623552073612922718416871603535535085523083939021",
"8700115492541474338049149013125102281865518624059015445617546140629435818912"
],
[
"10674973475340072635573101639867487770811074181475255667220644196793546640210",
"2926286967251299230490668407790788696102889214647256022788211245826267484824"
]
],
[
[
"9660441540778523475944706619139394922744328902833875392144658911530830074820",
"19548113127774514328631808547691096362144426239827206966690021428110281506546"
],
[
"1870837942477655969123169532603615788122896469891695773961478956740992497097",
"12536105729661705698805725105036536744930776470051238187456307227425796690780"
],
[
"21811903352654147452884857281720047789720483752548991551595462057142824037334",
"19021616763967199151052893283384285352200445499680068407023236283004353578353"
]
]
],
"IC": [
[
"11992897507809711711025355300535923222599547639134311050809253678876341466909",
"17181525095924075896332561978747020491074338784673526378866503154966799128110",
"1"
],
[
"17018665030246167677911144513385572506766200776123272044534328594850561667818",
"18601114175490465275436712413925513066546725461375425769709566180981674884464",
"1"
],
[
"18799470100699658367834559797874857804183288553462108031963980039244731716542",
"13064227487174191981628537974951887429496059857753101852163607049188825592007",
"1"
],
[
"17432501889058124609368103715904104425610382063762621017593209214189134571156",
"13406815149699834788256141097399354592751313348962590382887503595131085938635",
"1"
],
[
"10320964835612716439094703312987075811498239445882526576970512041988148264481",
"9024164961646353611176283204118089412001502110138072989569118393359029324867",
"1"
],
[
"718355081067365548229685160476620267257521491773976402837645005858953849298",
"14635482993933988261008156660773180150752190597753512086153001683711587601974",
"1"
],
[
"11777720285956632126519898515392071627539405001940313098390150593689568177535",
"8483603647274280691250972408211651407952870456587066148445913156086740744515",
"1"
]
]
}

View File

@ -3,55 +3,49 @@ package rln
import "C" import "C"
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/waku-org/go-zerokit-rln/rln/link" "github.com/waku-org/go-zerokit-rln/rln/link"
"github.com/waku-org/go-zerokit-rln/rln/resources"
) )
// Same as: https://github.com/vacp2p/zerokit/blob/v0.3.5/rln/src/public.rs#L35
// Prevents a RLN ZK proof generated for one application to be re-used in another one.
var RLN_IDENTIFIER = [32]byte{166, 140, 43, 8, 8, 22, 206, 113, 151, 128, 118, 40, 119, 197, 218, 174, 11, 117, 84, 228, 96, 211, 212, 140, 145, 104, 146, 99, 24, 192, 217, 4}
var DEFAULT_USER_MESSAGE_LIMIT = uint32(10)
// RLN represents the context used for rln. // RLN represents the context used for rln.
type RLN struct { type RLN struct {
w *link.RLNWrapper w *link.RLNWrapper
} }
func getResourcesFolder(depth TreeDepth) string {
return fmt.Sprintf("tree_height_%d", depth)
}
// NewRLN generates an instance of RLN. An instance supports both zkSNARKs logics // NewRLN generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. It uses a depth of 20 by default // and Merkle tree data structure and operations. It uses a depth of 20 by default
func NewRLN() (*RLN, error) { func NewRLN() (*RLN, error) {
wasm, err := resources.Asset("tree_height_20/rln.wasm") return NewWithConfig(DefaultTreeDepth, nil)
if err != nil {
return nil, err
}
zkey, err := resources.Asset("tree_height_20/rln_final.zkey")
if err != nil {
return nil, err
}
verifKey, err := resources.Asset("tree_height_20/verification_key.json")
if err != nil {
return nil, err
}
r := &RLN{}
depth := 20
r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey)
if err != nil {
return nil, err
}
return r, nil
} }
// NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics // NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree // and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree
func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLN, error) { func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig *TreeConfig) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error var err error
r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey) treeConfigBytes := []byte{}
if treeConfig != nil {
treeConfigBytes, err = json.Marshal(treeConfig)
if err != nil {
return nil, err
}
}
r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey, treeConfigBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -59,15 +53,21 @@ func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RL
return r, nil return r, nil
} }
// NewRLNWithFolder generates an instance of RLN. An instance supports both zkSNARKs logics // NewWithConfig generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `deptk` indicates the depth of Merkle tree // and Merkle tree data structure and operations. The parameter `depth` indicates the depth of Merkle tree
// The parameter “ func NewWithConfig(depth TreeDepth, treeConfig *TreeConfig) (*RLN, error) {
func NewRLNWithFolder(depth int, resourcesFolderPath string) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error var err error
r.w, err = link.NewWithFolder(depth, resourcesFolderPath) configBytes, err := json.Marshal(config{
ResourcesFolder: getResourcesFolder(depth),
TreeConfig: treeConfig,
})
if err != nil {
return nil, err
}
r.w, err = link.New(int(depth), configBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,12 +93,14 @@ func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
return nil return nil
} }
func toIdentityCredential(generatedKeys []byte) (*IdentityCredential, error) { func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*IdentityCredential, error) {
// add user message limit
key := &IdentityCredential{ key := &IdentityCredential{
IDTrapdoor: [32]byte{}, IDTrapdoor: [32]byte{},
IDNullifier: [32]byte{}, IDNullifier: [32]byte{},
IDSecretHash: [32]byte{}, IDSecretHash: [32]byte{},
IDCommitment: [32]byte{}, IDCommitment: [32]byte{},
UserMessageLimit: userMessageLimit,
} }
if len(generatedKeys) != 32*4 { if len(generatedKeys) != 32*4 {
@ -115,34 +117,63 @@ func toIdentityCredential(generatedKeys []byte) (*IdentityCredential, error) {
// MembershipKeyGen generates a IdentityCredential that can be used for the // MembershipKeyGen generates a IdentityCredential that can be used for the
// registration into the rln membership contract. Returns an error if the key generation fails // registration into the rln membership contract. Returns an error if the key generation fails
func (r *RLN) MembershipKeyGen() (*IdentityCredential, error) { // Accepts an optional parameter that sets the user message limit which defaults
// to DEFAULT_USER_MESSAGE_LIMIT
func (r *RLN) MembershipKeyGen(userMessageLimitParam ...uint32) (*IdentityCredential, error) {
var userMessageLimit uint32
if len(userMessageLimitParam) == 1 {
userMessageLimit = userMessageLimitParam[0]
} else if len(userMessageLimitParam) == 0 {
userMessageLimit = DEFAULT_USER_MESSAGE_LIMIT
} else {
return nil, errors.New("just one user message limit is allowed")
}
generatedKeys := r.w.ExtendedKeyGen() generatedKeys := r.w.ExtendedKeyGen()
if generatedKeys == nil { if generatedKeys == nil {
return nil, errors.New("error in key generation") return nil, errors.New("error in key generation")
} }
return toIdentityCredential(generatedKeys) return toIdentityCredential(generatedKeys, userMessageLimit)
} }
// SeededMembershipKeyGen generates a deterministic IdentityCredential using a seed // SeededMembershipKeyGen generates a deterministic IdentityCredential using a seed
// that can be used for the registration into the rln membership contract. // that can be used for the registration into the rln membership contract.
// Returns an error if the key generation fails // Returns an error if the key generation fails
func (r *RLN) SeededMembershipKeyGen(seed []byte) (*IdentityCredential, error) { // Accepts an optional parameter that sets the user message limit which defaults
// to DEFAULT_USER_MESSAGE_LIMIT
func (r *RLN) SeededMembershipKeyGen(seed []byte, userMessageLimitParam ...uint32) (*IdentityCredential, error) {
var userMessageLimit uint32
if len(userMessageLimitParam) == 1 {
userMessageLimit = userMessageLimitParam[0]
} else if len(userMessageLimitParam) == 0 {
userMessageLimit = DEFAULT_USER_MESSAGE_LIMIT
} else {
return nil, errors.New("just one user message limit is allowed")
}
generatedKeys := r.w.ExtendedSeededKeyGen(seed) generatedKeys := r.w.ExtendedSeededKeyGen(seed)
if generatedKeys == nil { if generatedKeys == nil {
return nil, errors.New("error in key generation") return nil, errors.New("error in key generation")
} }
return toIdentityCredential(generatedKeys) return toIdentityCredential(generatedKeys, userMessageLimit)
} }
// appendLength returns length prefixed version of the input with the following format // appendLength returns length prefixed version of the input with the following format
// [len<8>|input<var>], the len is a 8 byte value serialized in little endian // [len<8>|input<var>], the len is a 8 byte value serialized in little endian
func appendLength(input []byte) []byte { func appendLength(input []byte) []byte {
inputLen := make([]byte, 8) inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(input))) binary.LittleEndian.PutUint64(inputLen, uint64(len(input)))
return append(inputLen, input...) return append(inputLen, input...)
} }
// Similar to appendLength but for 32 byte values. The length that is prepended is
// the length of elements that are 32 bytes long each
func appendLength32(input []byte) []byte {
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(input)/32))
return append(inputLen, input...)
}
func (r *RLN) Sha256(data []byte) (MerkleNode, error) { func (r *RLN) Sha256(data []byte) (MerkleNode, error) {
lenPrefData := appendLength(data) lenPrefData := appendLength(data)
@ -176,65 +207,131 @@ func (r *RLN) Poseidon(input ...[]byte) (MerkleNode, error) {
return result, nil return result, nil
} }
func (r *RLN) ExtractMetadata(proof RateLimitProof) (ProofMetadata, error) {
externalNullifierRes, err := r.Poseidon(proof.Epoch[:], proof.RLNIdentifier[:])
if err != nil {
return ProofMetadata{}, fmt.Errorf("could not construct the external nullifier: %w", err)
}
return ProofMetadata{
Nullifier: proof.Nullifier,
ShareX: proof.ShareX,
ShareY: proof.ShareY,
ExternalNullifier: externalNullifierRes,
}, nil
}
// GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree. // GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree.
// The output will containt the proof data and should be parsed as |proof<128>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>| // The output will containt the proof data and should be parsed as |proof<128>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
// integers wrapped in <> indicate value sizes in bytes // integers wrapped in <> indicate value sizes in bytes
func (r *RLN) GenerateProof(data []byte, key IdentityCredential, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) { func (r *RLN) GenerateProof(
input := serialize(key.IDSecretHash, index, epoch, data) data []byte,
key IdentityCredential,
index MembershipIndex,
epoch Epoch,
messageId uint32) (*RateLimitProof, error) {
externalNullifierInput, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
if err != nil {
return nil, fmt.Errorf("could not construct the external nullifier: %w", err)
}
input := serialize(key.IDSecretHash, index, key.UserMessageLimit, messageId, externalNullifierInput, data)
proofBytes, err := r.w.GenerateRLNProof(input) proofBytes, err := r.w.GenerateRLNProof(input)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(proofBytes) != 320 { expectedBytes := 288
return nil, errors.New("invalid proof generated") if len(proofBytes) != expectedBytes {
return nil, fmt.Errorf("invalid proof generated. size: %d expected: %d",
len(proofBytes), expectedBytes)
} }
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ] // parse proof taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L750
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
proofOffset := 128 proofOffset := 128
rootOffset := proofOffset + 32 rootOffset := proofOffset + 32
epochOffset := rootOffset + 32 externalNullifierOffset := rootOffset + 32
shareXOffset := epochOffset + 32 shareXOffset := externalNullifierOffset + 32
shareYOffset := shareXOffset + 32 shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32 nullifierOffset := shareYOffset + 32
rlnIdentifierOffset := nullifierOffset + 32
var zkproof ZKSNARK var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode var proofRoot, shareX, shareY MerkleNode
var epochR Epoch var externalNullifier Nullifier
var nullifier Nullifier var nullifier Nullifier
var rlnIdentifier RLNIdentifier
copy(zkproof[:], proofBytes[0:proofOffset]) copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset]) copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
copy(epochR[:], proofBytes[rootOffset:epochOffset]) copy(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
copy(shareX[:], proofBytes[epochOffset:shareXOffset]) copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset])
copy(shareY[:], proofBytes[shareXOffset:shareYOffset]) copy(shareY[:], proofBytes[shareXOffset:shareYOffset])
copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset]) copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset])
copy(rlnIdentifier[:], proofBytes[nullifierOffset:rlnIdentifierOffset])
return &RateLimitProof{ return &RateLimitProof{
Proof: zkproof, Proof: zkproof,
MerkleRoot: proofRoot, MerkleRoot: proofRoot,
Epoch: epochR, ExternalNullifier: externalNullifier,
ShareX: shareX, ShareX: shareX,
ShareY: shareY, ShareY: shareY,
Nullifier: nullifier, Nullifier: nullifier,
RLNIdentifier: rlnIdentifier, }, nil
}
// Returns a RLN proof with a custom witness, so no tree is required in the RLN instance
// to calculate such proof. The witness can be created with GetMerkleProof data.
func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {
// serialized as: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L127
// input [ id_secret_hash<32> | user_message_limit<32> | message_id<32> | num_elements<8> | path_elements<var1> | num_indexes<8> | path_indexes<var2> | external_nullifier<32> ]
proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
if err != nil {
return nil, err
}
expectedBytes := 288
if len(proofBytes) != expectedBytes {
return nil, fmt.Errorf("invalid proof generated. size: %d expected: %d",
len(proofBytes), expectedBytes)
}
// parse proof taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L750
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
proofOffset := 128
rootOffset := proofOffset + 32
externalNullifierOffset := rootOffset + 32
shareXOffset := externalNullifierOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode
var externalNullifier Nullifier
var nullifier Nullifier
copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
copy(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset])
copy(shareY[:], proofBytes[shareXOffset:shareYOffset])
copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset])
return &RateLimitProof{
Proof: zkproof,
MerkleRoot: proofRoot,
ExternalNullifier: externalNullifier,
ShareX: shareX,
ShareY: shareY,
Nullifier: nullifier,
}, nil
}
func (r *RLN) CreateWitness(
idSecretHash IDSecretHash,
userMessageLimit uint32,
messageId uint32,
data []byte,
epoch [32]byte,
merkleProof MerkleProof) (RLNWitnessInput, error) {
externalNullifier, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
if err != nil {
return RLNWitnessInput{}, fmt.Errorf("could not construct the external nullifier: %w", err)
}
return RLNWitnessInput{
IDSecretHash: idSecretHash,
UserMessageLimit: userMessageLimit,
MessageId: messageId,
MerkleProof: merkleProof,
X: HashToBN255(data),
ExternalNullifier: externalNullifier,
}, nil }, nil
} }
@ -297,7 +394,7 @@ func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool
return false, err return false, err
} }
return bool(res), nil return res, nil
} }
// RecoverIDSecret returns an IDSecret having obtained before two proofs // RecoverIDSecret returns an IDSecret having obtained before two proofs
@ -313,15 +410,31 @@ func (r *RLN) RecoverIDSecret(proof1 RateLimitProof, proof2 RateLimitProof) (IDS
return result, nil return result, nil
} }
// InsertMember adds the member to the tree // InsertMember adds the member to the tree. The leaf is made of
func (r *RLN) InsertMember(idComm IDCommitment) error { // the id commitment and the user message limit
insertionSuccess := r.w.SetNextLeaf(idComm[:]) func (r *RLN) InsertMember(idComm IDCommitment, userMessageLimit uint32) error {
userMessageLimitBytes := SerializeUint32(userMessageLimit)
hashedLeaf, err := r.Poseidon(idComm[:], userMessageLimitBytes[:])
if err != nil {
return err
}
insertionSuccess := r.w.SetNextLeaf(hashedLeaf[:])
if !insertionSuccess { if !insertionSuccess {
return errors.New("could not insert member") return errors.New("could not insert member")
} }
return nil return nil
} }
func (r *RLN) InsertRawLeaf(rawLeaf MerkleNode) error {
insertionSuccess := r.w.SetNextLeaf(rawLeaf[:])
if !insertionSuccess {
return errors.New("could not insert raw leaf")
}
return nil
}
// Insert multiple members i.e., identity commitments starting from index // Insert multiple members i.e., identity commitments starting from index
// This proc is atomic, i.e., if any of the insertions fails, all the previous insertions are rolled back // This proc is atomic, i.e., if any of the insertions fails, all the previous insertions are rolled back
func (r *RLN) InsertMembers(index MembershipIndex, idComms []IDCommitment) error { func (r *RLN) InsertMembers(index MembershipIndex, idComms []IDCommitment) error {
@ -399,10 +512,31 @@ func (r *RLN) GetLeaf(index MembershipIndex) (IDCommitment, error) {
return result, nil return result, nil
} }
// GetMerkleProof returns the Merkle proof for the element at the specified index
// The output should be parsed as: num_elements<8>|path_elements<var1>|num_indexes<8>|path_indexes<var2>
// where num_elements indicate var1 array size and num_indexes indicate var2 array size.
// Both num_elements and num_indexes shall be equal and match the tree depth.
// A tree with depth 20 has 676 bytes = 8 + 32 * 20 + 8 + 20 * 1
// Proof elements are stored as little endian
func (r *RLN) GetMerkleProof(index MembershipIndex) (MerkleProof, error) {
proofBytes, err := r.w.GetMerkleProof(index)
if err != nil {
return MerkleProof{}, err
}
var result MerkleProof
err = result.deserialize(proofBytes)
if err != nil {
return MerkleProof{}, err
}
return result, nil
}
// AddAll adds members to the Merkle tree // AddAll adds members to the Merkle tree
func (r *RLN) AddAll(list []IDCommitment) error { func (r *RLN) AddAll(list []IdentityCredential) error {
for _, member := range list { for _, member := range list {
if err := r.InsertMember(member); err != nil { if err := r.InsertMember(member.IDCommitment, member.UserMessageLimit); err != nil {
return err return err
} }
} }
@ -444,7 +578,7 @@ func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
output = append(output, *keypair) output = append(output, *keypair)
// insert the key to the Merkle tree // insert the key to the Merkle tree
if err := rln.InsertMember(keypair.IDCommitment); err != nil { if err := rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit); err != nil {
return nil, MerkleNode{}, err return nil, MerkleNode{}, err
} }
} }
@ -481,3 +615,17 @@ func (r *RLN) AtomicOperation(index MembershipIndex, idCommsToInsert []IDCommitm
} }
return nil return nil
} }
// Flush
func (r *RLN) Flush() error {
success := r.w.Flush()
if !success {
return errors.New("cannot flush db")
}
return nil
}
// LeavesSet indicates how many elements have been inserted in the merkle tree
func (r *RLN) LeavesSet() uint {
return r.w.LeavesSet()
}

View File

@ -17,6 +17,23 @@ type RLNSuite struct {
suite.Suite suite.Suite
} }
func (s *RLNSuite) TestNew() {
rln, err := NewRLN()
s.NoError(err)
root1, err := rln.GetMerkleRoot()
s.NoError(err)
s.Len(root1, 32)
rln2, err := NewWithConfig(DefaultTreeDepth, nil)
s.NoError(err)
root2, err := rln2.GetMerkleRoot()
s.NoError(err)
s.Len(root2, 32)
s.Equal(root1, root2)
}
func (s *RLNSuite) TestMembershipKeyGen() { func (s *RLNSuite) TestMembershipKeyGen() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
@ -59,19 +76,57 @@ func (s *RLNSuite) TestInsertMember() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment) err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err) s.NoError(err)
} }
func (s *RLNSuite) TestInsertRawLeaf() {
rln, err := NewRLN()
s.NoError(err)
for i := 0; i < 10; i++ {
// Generate a membership
memKeys, err := rln.MembershipKeyGen(10)
s.NoError(err)
// Calculate the leaf ourselves
userMessageLimitBytes := SerializeUint32(memKeys.UserMessageLimit)
hashedLeaf, err := rln.Poseidon(memKeys.IDCommitment[:], userMessageLimitBytes[:])
s.NoError(err)
// Insert the leaf as it is
err = rln.InsertRawLeaf(hashedLeaf)
s.NoError(err)
// Get it from the tree
retrievedLeaf, err := rln.GetLeaf(uint(i))
s.NoError(err)
// Check the retrieved matches the one we added
s.Equal(hashedLeaf, retrievedLeaf)
// Check tree size matches
numLeaves := rln.LeavesSet()
s.Equal(uint(i+1), numLeaves)
}
}
func (s *RLNSuite) TestInsertMembers() { func (s *RLNSuite) TestInsertMembers() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
keypair, err := rln.MembershipKeyGen() var commitments []IDCommitment
for i := 0; i < 10; i++ {
keypair, err := rln.MembershipKeyGen()
s.NoError(err)
commitments = append(commitments, keypair.IDCommitment)
}
err = rln.InsertMembers(0, commitments)
s.NoError(err) s.NoError(err)
err = rln.InsertMembers(0, []IDCommitment{keypair.IDCommitment}) numLeaves := rln.LeavesSet()
s.NoError(err) s.Equal(uint(10), numLeaves)
} }
func (s *RLNSuite) TestRemoveMember() { func (s *RLNSuite) TestRemoveMember() {
@ -81,7 +136,7 @@ func (s *RLNSuite) TestRemoveMember() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment) err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err) s.NoError(err)
err = rln.DeleteMember(MembershipIndex(0)) err = rln.DeleteMember(MembershipIndex(0))
@ -99,7 +154,7 @@ func (s *RLNSuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment) err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err) s.NoError(err)
// read the Merkle Tree root after insertion // read the Merkle Tree root after insertion
@ -183,37 +238,64 @@ func (s *RLNSuite) TestCheckCorrectness() {
s.Equal(expectedRoot, root[:]) s.Equal(expectedRoot, root[:])
} }
func (s *RLNSuite) TestGetLeaf() {
rln, err := NewRLN()
s.NoError(err)
amountLeafs := int(31)
for i := 0; i < amountLeafs; i++ {
// allowed messages per epoch of the membership
// using different values between 1 and 7
userMessageLimit := uint32(amountLeafs%7 + 1)
// generate membership
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
// insert membership
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err)
// retrieve the leaf
retrievedLeaf, err := rln.GetLeaf(uint(i))
s.NoError(err)
// calculate the leaf we would expect
userMessageLimitBytes := SerializeUint32(userMessageLimit)
hashedLeaf, err := rln.Poseidon(memKeys.IDCommitment[:], userMessageLimitBytes[:])
s.NoError(err)
// assert it matches
s.Equal(hashedLeaf, retrievedLeaf)
}
}
func (s *RLNSuite) TestValidProof() { func (s *RLNSuite) TestValidProof() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
memKeys, err := rln.MembershipKeyGen() // allowed messages per epoch of the membership
s.NoError(err) userMessageLimit := uint32(10)
//peer's index in the Merkle Tree //peer's index in the Merkle Tree
index := uint(5) index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
// Create a Merkle tree with random members // Create a Merkle tree with random members
for i := uint(0); i < 10; i++ { for i := uint(0); i < 10; i++ {
if i == index { if i == index {
// insert the current peer's pk err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
err = rln.InsertMember(memKeys.IDCommitment)
s.NoError(err) s.NoError(err)
fifthIndexLeaf, err := rln.GetLeaf(index)
s.NoError(err)
s.Equal(memKeys.IDCommitment, fifthIndexLeaf)
} else { } else {
// create a new key pair // create a new key pair
memberKeys, err := rln.MembershipKeyGen() memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment) err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
leaf, err := rln.GetLeaf(i)
s.NoError(err)
s.Equal(memberKeys.IDCommitment, leaf)
} }
} }
@ -221,24 +303,75 @@ func (s *RLNSuite) TestValidProof() {
msg := []byte("Hello") msg := []byte("Hello")
// prepare the epoch // prepare the epoch
var epoch Epoch var epoch Epoch = SerializeUint32(1000)
// generate proof // generate multiple valid proofs for the same epoch
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch) for i := uint32(0); i < userMessageLimit; i++ {
// message sequence within the epoch
messageId := uint32(i)
// generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
s.NoError(err)
// verify the proof
verified, err := rln.Verify(msg, *proofRes)
s.NoError(err)
s.True(verified)
// verify with roots
root, err := rln.GetMerkleRoot()
s.NoError(err)
verified, err = rln.Verify(msg, *proofRes, root)
s.NoError(err)
s.True(verified)
}
}
func (s *RLNSuite) TestProofBeyondLimit() {
rln, err := NewRLN()
s.NoError(err) s.NoError(err)
// verify the proof // allowed messages per epoch of the membership
verified, err := rln.Verify(msg, *proofRes) userMessageLimit := uint32(10)
s.NoError(err)
s.True(verified)
// verify with roots //peer's index in the Merkle Tree
root, err := rln.GetMerkleRoot() index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
verified, err = rln.Verify(msg, *proofRes, root) // Create a Merkle tree with random members
s.NoError(err) for i := uint(0); i < 10; i++ {
s.True(verified) if i == index {
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
}
}
// prepare the message
msg := []byte("Hello")
// prepare the epoch
var epoch Epoch = SerializeUint32(876543456)
// TODO;:
for i := uint32(userMessageLimit + 1); i < (userMessageLimit + 10); i++ {
// message sequence within the epoch
messageId := uint32(i)
// generate proof TODO:Errors!
_, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
s.Error(err)
}
} }
func (s *RLNSuite) TestInvalidProof() { func (s *RLNSuite) TestInvalidProof() {
@ -255,14 +388,14 @@ func (s *RLNSuite) TestInvalidProof() {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if i == index { if i == index {
// insert the current peer's pk // insert the current peer's pk
err := rln.InsertMember(memKeys.IDCommitment) err := rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
} else { } else {
// create a new key pair // create a new key pair
memberKeys, err := rln.MembershipKeyGen() memberKeys, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment) err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
} }
} }
@ -278,8 +411,11 @@ func (s *RLNSuite) TestInvalidProof() {
badIndex := 4 badIndex := 4
// message sequence within the epoch
messageId := uint32(1)
// generate proof // generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch) proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch, messageId)
s.NoError(err) s.NoError(err)
// verify the proof (should not be verified) // verify the proof (should not be verified)
@ -288,6 +424,232 @@ func (s *RLNSuite) TestInvalidProof() {
s.False(verified) s.False(verified)
} }
func (s *RLNSuite) TestGetMerkleProof() {
for _, treeDepth := range []TreeDepth{TreeDepth15, TreeDepth19, TreeDepth20} {
treeDepthInt := int(treeDepth)
rln, err := NewWithConfig(treeDepth, nil)
s.NoError(err)
leaf0 := [32]byte{0x00}
leaf1 := [32]byte{0x01}
leaf5 := [32]byte{0x05}
rln.InsertMemberAt(0, leaf0)
rln.InsertMemberAt(1, leaf1)
rln.InsertMemberAt(5, leaf5)
b1, err := rln.GetMerkleProof(0)
s.NoError(err)
s.Equal(treeDepthInt, len(b1.PathElements))
s.Equal(treeDepthInt, len(b1.PathIndexes))
// First path is right leaf [0, 1]
s.EqualValues(leaf1, b1.PathElements[0])
b2, err := rln.GetMerkleProof(4)
s.NoError(err)
s.Equal(treeDepthInt, len(b2.PathElements))
s.Equal(treeDepthInt, len(b2.PathIndexes))
// First path is right leaf [4, 5]
s.EqualValues(leaf5, b2.PathElements[0])
b3, err := rln.GetMerkleProof(10)
s.NoError(err)
s.Equal(treeDepthInt, len(b3.PathElements))
s.Equal(treeDepthInt, len(b3.PathIndexes))
// First path is right leaf. But its empty
s.EqualValues([32]byte{0x00}, b3.PathElements[0])
}
}
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
treeSize := 20
userMessageLimit := uint32(100)
message := []byte("some rln protected message")
rln, err := NewRLN()
s.NoError(err)
treeElements := make([]IdentityCredential, 0)
// Create a Merkle tree with random members
for i := 0; i < treeSize; i++ {
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
treeElements = append(treeElements, *memberKeys)
}
// For different leafs (with a custom witness)
for _, memberIndex := range []uint{0, 10, 13} {
root, err := rln.GetMerkleRoot()
s.NoError(err)
// We provide out custom witness
merkleProof, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
// For different epochs
for _, epoch := range []Epoch{ToEpoch(1), ToEpoch(9998765)} {
// For some possible message ids
for _, messageId := range []uint32{0, 50, 99} {
rlnWitness, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
// Generate a proof with our custom witness (Merkle Path of the memberIndex)
proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness)
s.NoError(err)
verified1, err := rln.Verify(message, *proofRes1, root)
s.NoError(err)
s.True(verified1)
// Generate a proof without our custom witness, to ensure they match
proofRes2, err := rln.GenerateProof(message, treeElements[memberIndex], MembershipIndex(memberIndex), epoch, messageId)
s.NoError(err)
// Ensure we have the same root
s.Equal(root, proofRes1.MerkleRoot)
// Proof generate with custom witness match the proof generate with the witness
// from zerokit. Proof itself is not asserted, can be different.
s.Equal(proofRes1.MerkleRoot, proofRes2.MerkleRoot)
s.Equal(proofRes1.ExternalNullifier, proofRes2.ExternalNullifier)
s.Equal(proofRes1.ShareX, proofRes2.ShareX)
s.Equal(proofRes1.ShareY, proofRes2.ShareY)
s.Equal(proofRes1.Nullifier, proofRes2.Nullifier)
}
}
}
}
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() {
treeSize := 20
rln, err := NewRLN()
s.NoError(err)
treeElements := make([]IdentityCredential, 0)
// Create a Merkle tree with random members
for i := 0; i < treeSize; i++ {
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
treeElements = append(treeElements, *memberKeys)
}
// We generate proofs with a custom witness aquired outside zerokit for diferent indexes
for _, memberIndex := range []uint{0, 10, 13, 15} {
root, err := rln.GetMerkleRoot()
s.NoError(err)
// We provide out custom witness
merkleProof, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
message := []byte("some rln protected message")
epoch := ToEpoch(1000)
userMessageLimit := uint32(10)
messageId := uint32(1)
rlnWitness1, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
// Generate a proof with our custom witness (Merkle Path of the memberIndex)
proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness1)
s.NoError(err)
// 1) Message changed, does not verify
verified1, err := rln.Verify([]byte("different message"), *proofRes1, root)
s.NoError(err)
s.False(verified1)
// 2) Different nullifier (epoch or rln id), does not verify
proofRes1.ExternalNullifier = [32]byte{0x11}
verified2, err := rln.Verify(message, *proofRes1, root)
s.NoError(err)
s.False(verified2)
// 3) Merkle proof in provided witness is wrong, does not verify
merkleProof.PathElements[0] = [32]byte{0x11}
rlnWitness2, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
proofRes3, err := rln.GenerateRLNProofWithWitness(rlnWitness2)
s.NoError(err)
verified3, err := rln.Verify(message, *proofRes3, root)
s.NoError(err)
s.False(verified3)
// 4) Membership does not match the index (and not part of tree), does not verify
merkleProof4, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
// Membership that does not match the index
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
// Proof proves memberIndex inclusion, but provided membership is different
rlnWitness4, err := rln.CreateWitness(
memberKeys.IDSecretHash,
userMessageLimit,
messageId,
[]byte("some rln protected message"),
ToEpoch(999),
merkleProof4)
s.NoError(err)
proofRes4, err := rln.GenerateRLNProofWithWitness(rlnWitness4)
s.NoError(err)
verified4, err := rln.Verify(message, *proofRes4, root)
s.NoError(err)
s.False(verified4)
// 5) Message id goes beyond the userMessageLimit, does not generate
wrongMessageId := uint32(1000)
rlnWitness5, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
wrongMessageId,
message,
epoch,
merkleProof)
s.NoError(err)
_, err = rln.GenerateRLNProofWithWitness(rlnWitness5)
s.Error(err)
}
}
func (s *RLNSuite) TestEpochConsistency() { func (s *RLNSuite) TestEpochConsistency() {
// check edge cases // check edge cases
var epoch uint64 = math.MaxUint64 var epoch uint64 = math.MaxUint64

View File

@ -1,20 +1,37 @@
package rln package rln
import "encoding/binary" import (
"encoding/binary"
"errors"
"fmt"
"math/big"
)
// serialize converts a RateLimitProof and the data to a byte seq // serialize converts a RateLimitProof and the data to a byte seq
// this conversion is used in the proofGen function // format taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L747
// the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146 // [identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ]
// [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ] func serialize(
func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg []byte) []byte { idKey IDSecretHash,
memIndex MembershipIndex,
userMessageLimit uint32,
messageId uint32,
externalNullifier [32]byte,
msg []byte) []byte {
memIndexBytes := make([]byte, 8) memIndexBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex)) binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex))
lenPrefMsg := appendLength(msg) lenPrefMsg := appendLength(msg)
var userMessageLimitByte [32]byte
var messageIdByte [32]byte
binary.LittleEndian.PutUint32(userMessageLimitByte[0:], userMessageLimit)
binary.LittleEndian.PutUint32(messageIdByte[0:], messageId)
output := append(idKey[:], memIndexBytes...) output := append(idKey[:], memIndexBytes...)
output = append(output, epoch[:]...) output = append(output, userMessageLimitByte[:]...)
output = append(output, messageIdByte[:]...)
output = append(output, externalNullifier[:]...)
output = append(output, lenPrefMsg...) output = append(output, lenPrefMsg...)
return output return output
@ -23,7 +40,7 @@ func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg []
// serialize converts a RateLimitProof and data to a byte seq // serialize converts a RateLimitProof and data to a byte seq
// this conversion is used in the proof verification proc // this conversion is used in the proof verification proc
// the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205 // the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
// [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ] // [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal<var> ]
func (r RateLimitProof) serializeWithData(data []byte) []byte { func (r RateLimitProof) serializeWithData(data []byte) []byte {
lenPrefMsg := appendLength(data) lenPrefMsg := appendLength(data)
proofBytes := r.serialize() proofBytes := r.serialize()
@ -32,13 +49,102 @@ func (r RateLimitProof) serializeWithData(data []byte) []byte {
} }
// serialize converts a RateLimitProof to a byte seq // serialize converts a RateLimitProof to a byte seq
// [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> // [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
func (r RateLimitProof) serialize() []byte { func (r RateLimitProof) serialize() []byte {
proofBytes := append(r.Proof[:], r.MerkleRoot[:]...) proofBytes := append(r.Proof[:], r.MerkleRoot[:]...)
proofBytes = append(proofBytes, r.Epoch[:]...) proofBytes = append(proofBytes, r.ExternalNullifier[:]...)
proofBytes = append(proofBytes, r.ShareX[:]...) proofBytes = append(proofBytes, r.ShareX[:]...)
proofBytes = append(proofBytes, r.ShareY[:]...) proofBytes = append(proofBytes, r.ShareY[:]...)
proofBytes = append(proofBytes, r.Nullifier[:]...) proofBytes = append(proofBytes, r.Nullifier[:]...)
proofBytes = append(proofBytes, r.RLNIdentifier[:]...)
return proofBytes return proofBytes
} }
// serialize converts a RLNWitnessInput to a byte seq
// [ id_secret_hash<32> | user_message_limit<32> | message_id<32> | num_elements<8> | path_elements<var1> | num_indexes<8> | path_indexes<var2> | external_nullifier<32> ]
func (r *RLNWitnessInput) serialize() []byte {
output := make([]byte, 0)
var userMessageLimitByte [32]byte
var messageIdByte [32]byte
binary.LittleEndian.PutUint32(userMessageLimitByte[0:], r.UserMessageLimit)
binary.LittleEndian.PutUint32(messageIdByte[0:], r.MessageId)
output = append(output, r.IDSecretHash[:]...)
output = append(output, userMessageLimitByte[:]...)
output = append(output, messageIdByte[:]...)
output = append(output, r.MerkleProof.serialize()...)
output = append(output, r.X[:]...)
output = append(output, r.ExternalNullifier[:]...)
return output
}
func (r *RLNWitnessInput) deserialize(b []byte) error {
return errors.New("not implemented")
}
func (r *MerkleProof) serialize() []byte {
output := make([]byte, 0)
output = append(output, appendLength32(Flatten(r.PathElements))...)
output = append(output, appendLength(r.PathIndexes)...)
return output
}
func (r *MerkleProof) deserialize(b []byte) error {
// Check if we can read the first byte
if len(b) < 8 {
return errors.New(fmt.Sprintf("wrong input size: %d", len(b)))
}
var numElements big.Int
var numIndexes big.Int
offset := 0
// Get amounf of elements in the proof
numElements.SetBytes(revert(b[offset : offset+8]))
offset += 8
// With numElements we can determine the expected length of the proof.
expectedLen := 8 + int(32*numElements.Uint64()) + 8 + int(numElements.Uint64())
if len(b) != expectedLen {
return errors.New(fmt.Sprintf("wrong input size expected: %d, current: %d",
expectedLen,
len(b)))
}
r.PathElements = make([]MerkleNode, numElements.Uint64())
for i := uint64(0); i < numElements.Uint64(); i++ {
copy(r.PathElements[i][:], b[offset:offset+32])
offset += 32
}
// Get amount of indexes in the path
numIndexes.SetBytes(revert(b[offset : offset+8]))
offset += 8
// Both numElements and numIndexes shall be equal and match the tree depth.
if numIndexes.Uint64() != numElements.Uint64() {
return errors.New(fmt.Sprintf("amount of values in path and indexes do not match: %s vs %s",
numElements.String(), numIndexes.String()))
}
r.PathIndexes = make([]uint8, numIndexes.Uint64())
for i := uint64(0); i < numIndexes.Uint64(); i++ {
r.PathIndexes[i] = b[offset]
offset += 1
}
if offset != len(b) {
return errors.New(
fmt.Sprintf("error parsing proof read: %d, length; %d", offset, len(b)))
}
return nil
}

65
rln/serialize_test.go Normal file
View File

@ -0,0 +1,65 @@
package rln
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func random32() [32]byte {
var randomBytes [32]byte
_, _ = rand.Read(randomBytes[:])
return randomBytes
}
func TestMerkleProofSerDe(t *testing.T) {
for _, testSize := range []int{0, 1, 8, 16, 20} {
mProof := MerkleProof{
PathElements: []MerkleNode{},
PathIndexes: []uint8{},
}
for i := 0; i < testSize; i++ {
mProof.PathElements = append(mProof.PathElements, random32())
mProof.PathIndexes = append(mProof.PathIndexes, uint8(i%2))
}
// Check the size is the expected
ser := mProof.serialize()
require.Equal(t, 8+testSize*32+testSize+8, len(ser))
// Deserialize and check its matches the original
desProof := MerkleProof{}
err := desProof.deserialize(ser)
require.NoError(t, err)
require.Equal(t, mProof, desProof)
}
}
func TestRLNWitnessInputSerDe(t *testing.T) {
depth := 20
mProof := MerkleProof{
PathElements: []MerkleNode{},
PathIndexes: []uint8{},
}
for i := 0; i < depth; i++ {
mProof.PathElements = append(mProof.PathElements, random32())
mProof.PathIndexes = append(mProof.PathIndexes, uint8(i%2))
}
witness := RLNWitnessInput{
IDSecretHash: random32(),
UserMessageLimit: 8,
MessageId: 7,
MerkleProof: mProof,
X: [32]byte{0x00},
ExternalNullifier: [32]byte{0x00},
}
ser := witness.serialize()
require.Equal(t, 32+32+32+8+depth*32+depth+8+32+32, len(ser))
}

View File

@ -3,6 +3,7 @@ package rln
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json"
"time" "time"
) )
@ -35,12 +36,19 @@ type IdentityCredential = struct {
// Poseidon hash function implemented in rln lib // Poseidon hash function implemented in rln lib
// more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership // more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
IDCommitment IDCommitment `json:"idCommitment"` IDCommitment IDCommitment `json:"idCommitment"`
// user's allowed messages per epoch, added in RLN v2
UserMessageLimit uint32 `json:"userMessageLimit"`
} }
func IdentityCredentialEquals(i IdentityCredential, i2 IdentityCredential) bool { func IdentityCredentialEquals(i IdentityCredential, i2 IdentityCredential) bool {
return bytes.Equal(i.IDTrapdoor[:], i2.IDTrapdoor[:]) && bytes.Equal(i.IDNullifier[:], i2.IDNullifier[:]) && bytes.Equal(i.IDSecretHash[:], i2.IDSecretHash[:]) && bytes.Equal(i.IDCommitment[:], i2.IDCommitment[:]) return bytes.Equal(i.IDTrapdoor[:], i2.IDTrapdoor[:]) &&
bytes.Equal(i.IDNullifier[:], i2.IDNullifier[:]) &&
bytes.Equal(i.IDSecretHash[:], i2.IDSecretHash[:]) &&
bytes.Equal(i.IDCommitment[:], i2.IDCommitment[:]) &&
i.UserMessageLimit == i2.UserMessageLimit
} }
// Equivalent plus proof: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L52
type RateLimitProof struct { type RateLimitProof struct {
// RateLimitProof holds the public inputs to rln circuit as // RateLimitProof holds the public inputs to rln circuit as
// defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs // defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs
@ -49,7 +57,7 @@ type RateLimitProof struct {
// the root of Merkle tree used for the generation of the `proof` // the root of Merkle tree used for the generation of the `proof`
MerkleRoot MerkleNode `json:"root"` MerkleRoot MerkleNode `json:"root"`
// the epoch used for the generation of the `proof` // the epoch used for the generation of the `proof`
Epoch Epoch `json:"epoch"` ExternalNullifier Nullifier `json:"external_nullifier"`
// shareX and shareY are shares of user's identity key // shareX and shareY are shares of user's identity key
// these shares are created using Shamir secret sharing scheme // these shares are created using Shamir secret sharing scheme
// see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS // see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS
@ -58,8 +66,68 @@ type RateLimitProof struct {
// nullifier enables linking two messages published during the same epoch // nullifier enables linking two messages published during the same epoch
// see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers // see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers
Nullifier Nullifier `json:"nullifier"` Nullifier Nullifier `json:"nullifier"`
// Application specific RLN Identifier }
RLNIdentifier RLNIdentifier `json:"rlnIdentifier"`
type MerkleProof struct {
PathElements []MerkleNode `json:"pathElements"`
PathIndexes []uint8 `json:"pathIndexes"`
}
// Equivalent: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L35
type RLNWitnessInput struct {
IDSecretHash IDSecretHash `json:"identitySecretHash"`
UserMessageLimit uint32 `json:"userMessageLimit"`
MessageId uint32 `json:"messageId"`
MerkleProof MerkleProof `json:"merkleProof"`
X [32]byte `json:"x"`
ExternalNullifier Nullifier `json:"externalNullifier"`
}
type TreeDepth int
const (
TreeDepth20 TreeDepth = 20
TreeDepth15 TreeDepth = 15
TreeDepth19 TreeDepth = 19
)
const DefaultTreeDepth = TreeDepth20
type TreeMode string
const (
HighThroughput TreeMode = "HighThroughput"
LowSpace TreeMode = "LowSpace"
)
type TreeConfig struct {
CacheCapacity int
Mode TreeMode
Compression bool
FlushInterval time.Duration
Path string
}
func (t TreeConfig) MarshalJSON() ([]byte, error) {
output := struct {
CacheCapacity int `json:"cache_capacity"`
Mode TreeMode `json:"mode"`
Compression bool `json:"compression"`
FlushInterval uint `json:"flush_every_ms"`
Path string `json:"path"`
}{
CacheCapacity: t.CacheCapacity,
Mode: t.Mode,
Compression: t.Compression,
FlushInterval: uint(t.FlushInterval) / uint(time.Millisecond),
Path: t.Path,
}
return json.Marshal(output)
}
type config struct {
ResourcesFolder string `json:"resources_folder"`
TreeConfig *TreeConfig `json:"tree_config,omitempty"`
} }
type MembershipIndex = uint type MembershipIndex = uint
@ -106,8 +174,6 @@ func init() {
// the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here // the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
const STATIC_GROUP_MERKLE_ROOT = "ca7290e49680fa14eeaeea709e4742a8a074a1bcbfd50a4b3976742ae8a6ca25" const STATIC_GROUP_MERKLE_ROOT = "ca7290e49680fa14eeaeea709e4742a8a074a1bcbfd50a4b3976742ae8a6ca25"
const EPOCH_UNIT_SECONDS = uint64(10) // the rln-relay epoch length in seconds
type Epoch [32]byte type Epoch [32]byte
func BytesToEpoch(b []byte) Epoch { func BytesToEpoch(b []byte) Epoch {
@ -127,13 +193,13 @@ func (e Epoch) Uint64() uint64 {
} }
// CalcEpoch returns the corresponding rln `Epoch` value for a time.Time // CalcEpoch returns the corresponding rln `Epoch` value for a time.Time
func CalcEpoch(t time.Time) Epoch { func CalcEpoch(t time.Time, epochSize uint64) Epoch {
return ToEpoch(uint64(t.Unix()) / EPOCH_UNIT_SECONDS) return ToEpoch(uint64(t.Unix()) / epochSize)
} }
// GetCurrentEpoch gets the current rln Epoch time // GetCurrentEpoch gets the current rln Epoch time
func GetCurrentEpoch() Epoch { func GetCurrentEpoch(epochSize uint64) Epoch {
return CalcEpoch(time.Now()) return CalcEpoch(time.Now(), epochSize)
} }
// Diff returns the difference between the two rln `Epoch`s `e1` and `e2` // Diff returns the difference between the two rln `Epoch`s `e1` and `e2`
@ -143,6 +209,6 @@ func Diff(e1, e2 Epoch) int64 {
return int64(epoch1) - int64(epoch2) return int64(epoch1) - int64(epoch2)
} }
func (e Epoch) Time() time.Time { func (e Epoch) Time(epochSize uint64) time.Time {
return time.Unix(int64(e.Uint64()*EPOCH_UNIT_SECONDS), 0) return time.Unix(int64(e.Uint64()*epochSize), 0)
} }

View File

@ -1,7 +1,14 @@
package rln package rln
import ( import (
"encoding/binary"
"encoding/hex" "encoding/hex"
"hash"
"math/big"
"sync"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"golang.org/x/crypto/sha3"
) )
func ToIdentityCredentials(groupKeys [][]string) ([]IdentityCredential, error) { func ToIdentityCredentials(groupKeys [][]string) ([]IdentityCredential, error) {
@ -55,6 +62,14 @@ func Bytes128(b []byte) [128]byte {
return result return result
} }
func Flatten(b [][32]byte) []byte {
result := make([]byte, len(b)*32)
for i, v := range b {
copy(result[i*32:(i+1)*32], v[:])
}
return result
}
func ToBytes32LE(hexStr string) ([32]byte, error) { func ToBytes32LE(hexStr string) ([32]byte, error) {
b, err := hex.DecodeString(hexStr) b, err := hex.DecodeString(hexStr)
@ -62,9 +77,100 @@ func ToBytes32LE(hexStr string) ([32]byte, error) {
return [32]byte{}, err return [32]byte{}, err
} }
for i := 0; i < len(b)/2; i++ { bLen := len(b)
b[i], b[len(b)-i-1] = b[len(b)-i-1], b[i] for i := 0; i < bLen/2; i++ {
b[i], b[bLen-i-1] = b[bLen-i-1], b[i]
} }
return Bytes32(b), nil return Bytes32(b), nil
} }
func revert(b []byte) []byte {
bLen := len(b)
for i := 0; i < bLen/2; i++ {
b[i], b[bLen-i-1] = b[bLen-i-1], b[i]
}
return b
}
// BigIntToBytes32 takes a *big.Int (which uses big endian) and converts it into a little endian 32 byte array
// Notice that is the *big.Int value contains an integer <= 2^248 - 1 (a 7 bytes value with all bits on), it will right-pad the result with 0s until
// the result has 32 bytes, i.e.:
// for a some bigInt whose `Bytes()` are {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF}, using this function will return
// {0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
func BigIntToBytes32(value *big.Int) [32]byte {
b := revert(value.Bytes())
tmp := make([]byte, 32)
copy(tmp[0:len(b)], b)
return Bytes32(tmp)
}
// Bytes32ToBigInt takes a little endian 32 byte array and returns a *big.Int (which uses big endian)
func Bytes32ToBigInt(value [32]byte) *big.Int {
b := revert(value[:])
result := new(big.Int)
result.SetBytes(b)
return result
}
// Keccak functions take from here. To avoid unnecessary dependency to go-ethereum.
// https://github.com/ethereum/go-ethereum/blob/v1.13.11/crypto/crypto.go#L62-L84
// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type KeccakState interface {
hash.Hash
Read([]byte) (int, error)
}
// Avoids multiple allocations if used frequently
var keccak256Pool = sync.Pool{New: func() interface{} {
return NewKeccakState()
}}
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
return sha3.NewLegacyKeccak256().(KeccakState)
}
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
b := make([]byte, 32)
h, ok := keccak256Pool.Get().(KeccakState)
if !ok {
h = NewKeccakState()
}
defer keccak256Pool.Put(h)
h.Reset()
for _, b := range data {
h.Write(b)
}
h.Read(b)
return b
}
// Hashes a byte array to a field element in BN254, as used by zerokit.
// Equivalent to: https://github.com/vacp2p/zerokit/blob/v0.3.4/rln/src/hashers.rs
func HashToBN255(data []byte) [32]byte {
// Hash is fixed to 32 bytes
hashed := Keccak256(data[:])
// Convert to field element
var frBN254 fr.Element
frBN254.Unmarshal(revert(hashed))
frBN254Bytes := frBN254.Bytes()
// Return fixed size
fixexLen := [32]byte{}
copy(fixexLen[:], revert(frBN254Bytes[:]))
return fixexLen
}
func SerializeUint32(input uint32) [32]byte {
var byte32Type [32]byte
binary.LittleEndian.PutUint32(byte32Type[0:], input)
return byte32Type
}

53
rln/utils_test.go Normal file
View File

@ -0,0 +1,53 @@
package rln
import (
"bytes"
"math/big"
"testing"
"github.com/stretchr/testify/require"
)
func TestBigInt(t *testing.T) {
base := big.NewInt(2)
value := base.Exp(base, big.NewInt(248), nil)
value = value.Sub(value, big.NewInt(1)) // 2^248 - 1
b32Value := BigIntToBytes32(value)
require.True(t, bytes.Equal(b32Value[:], []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0}))
newValue := Bytes32ToBigInt(b32Value)
require.True(t, bytes.Equal(newValue.Bytes(), value.Bytes()))
}
func TestFlatten(t *testing.T) {
in1 := [][32]byte{[32]byte{}}
in2 := [][32]byte{[32]byte{0x00}, [32]byte{0x01}}
in3 := [][32]byte{[32]byte{0x01, 0x02, 0x03}, [32]byte{0x04, 0x05, 0x06}}
expected1 := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
expected2 := []byte{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
expected3 := []byte{
0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x4, 0x5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
out1 := Flatten(in1)
require.Equal(t, expected1, out1)
out2 := Flatten(in2)
require.Equal(t, expected2, out2)
out3 := Flatten(in3)
require.Equal(t, expected3, out3)
}
func TestHashToBN255(t *testing.T) {
// Inputs for proof generation
msg := []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
out := HashToBN255(msg)
require.Equal(t,
[32]byte{69, 7, 140, 46, 26, 131, 147, 30, 161, 68, 2, 5, 234, 195, 227, 223, 119, 187, 116, 97, 153, 70, 71, 254, 60, 149, 54, 109, 77, 79, 105, 20},
out)
}