Compare commits

..

No commits in common. "master" and "v0.1.11" have entirely different histories.

20 changed files with 651 additions and 1564 deletions

View File

@ -1,20 +0,0 @@
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,7 +13,3 @@
# 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,82 +1,3 @@
# 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,23 +1,18 @@
module github.com/waku-org/go-zerokit-rln module github.com/waku-org/go-zerokit-rln
go 1.19 go 1.18
require ( require (
github.com/consensys/gnark-crypto v0.12.1 github.com/stretchr/testify v1.7.2
github.com/sirupsen/logrus v1.9.3 github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327
github.com/stretchr/testify v1.8.4 github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230331223149-f90e66aebb0d
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240529153423-5df5db48b69f github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230331181847-cba74520bae9
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
github.com/rogpeppe/go-internal v1.9.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // 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
) )

55
go.sum
View File

@ -1,53 +1,28 @@
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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331223042-3d24f0c813c3 h1:9V9Za2c8vaWM2ta/hrLibqUQw/KRgdZQdKejn/Q9exo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331223042-3d24f0c813c3/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176 h1:ezeAofaW3B6tfqS06FwKAKKXpNkimWnIwKjDU0dDPKE= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327 h1:Q5XQqo+PEmvrybT8D7BEsKCwIYDi80s+00Q49cfm9Gs=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c h1:/eGH8EAt5/zGfNRBQ0nJMrfZDeXRSJrm8E8uCPlsC3A= github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230331223149-f90e66aebb0d h1:Kcg85Y2xGU6hqZ/kMfkLQF2jAog8vt+tw1/VNidzNtE=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230331223149-f90e66aebb0d/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240528140707-ed6b40a98d7b h1:LEa2s1p+Z8SN475dVr3XDmvmGyKzIDKPcAQ+6hTyVwA= github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230331181847-cba74520bae9 h1:u+YUlWDltHiK5upSb7M6mStc84zdc4vTCNNOz7R5RaY=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240528140707-ed6b40a98d7b/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230331181847-cba74520bae9/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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,54 +5,32 @@
package link package link
import ( import r "github.com/waku-org/go-zerokit-rln-apple/rln"
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, treeConfig []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func New(depth int, config []byte) (*RLNWrapper, error) { func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config) rln, err := r.NewWithFolder(depth, resourcesFolderPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func (i RLNWrapper) SetTree(treeHeight uint) bool {
return i.ffi.SetTree(treeHeight)
}
func (i RLNWrapper) InitTreeWithLeaves(idcommitments []byte) bool {
return i.ffi.InitTreeWithLeaves(idcommitments)
}
func (i RLNWrapper) KeyGen() []byte {
return i.ffi.KeyGen()
}
func (i RLNWrapper) SeededKeyGen(seed []byte) []byte {
return i.ffi.SeededKeyGen(seed)
}
func (i RLNWrapper) ExtendedKeyGen() []byte { func (i RLNWrapper) ExtendedKeyGen() []byte {
return i.ffi.ExtendedKeyGen() return i.ffi.ExtendedKeyGen()
} }
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) { func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input) return i.ffi.Hash(input)
} }
@ -61,10 +39,6 @@ func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
return i.ffi.PoseidonHash(input) return i.ffi.PoseidonHash(input)
} }
func (i RLNWrapper) SetLeaf(index uint, idcommitment []byte) bool {
return i.ffi.SetLeaf(index, idcommitment)
}
func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool { func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool {
return i.ffi.SetNextLeaf(idcommitment) return i.ffi.SetNextLeaf(idcommitment)
} }
@ -81,50 +55,10 @@ func (i RLNWrapper) GetRoot() ([]byte, error) {
return i.ffi.GetRoot() return i.ffi.GetRoot()
} }
func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
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)
} }
func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) bool {
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) {
return i.ffi.RecoverIDSecret(proof1, proof2)
}
func (i RLNWrapper) SetMetadata(metadata []byte) bool {
return i.ffi.SetMetadata(metadata)
}
func (i RLNWrapper) GetMetadata() ([]byte, error) {
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,54 +4,32 @@
package link package link
import ( import r "github.com/waku-org/go-zerokit-rln-arm/rln"
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, treeConfig []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func New(depth int, config []byte) (*RLNWrapper, error) { func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config) rln, err := r.NewWithFolder(depth, resourcesFolderPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func (i RLNWrapper) SetTree(treeHeight uint) bool {
return i.ffi.SetTree(treeHeight)
}
func (i RLNWrapper) InitTreeWithLeaves(idcommitments []byte) bool {
return i.ffi.InitTreeWithLeaves(idcommitments)
}
func (i RLNWrapper) KeyGen() []byte {
return i.ffi.KeyGen()
}
func (i RLNWrapper) SeededKeyGen(seed []byte) []byte {
return i.ffi.SeededKeyGen(seed)
}
func (i RLNWrapper) ExtendedKeyGen() []byte { func (i RLNWrapper) ExtendedKeyGen() []byte {
return i.ffi.ExtendedKeyGen() return i.ffi.ExtendedKeyGen()
} }
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) { func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input) return i.ffi.Hash(input)
} }
@ -60,10 +38,6 @@ func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
return i.ffi.PoseidonHash(input) return i.ffi.PoseidonHash(input)
} }
func (i RLNWrapper) SetLeaf(index uint, idcommitment []byte) bool {
return i.ffi.SetLeaf(index, idcommitment)
}
func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool { func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool {
return i.ffi.SetNextLeaf(idcommitment) return i.ffi.SetNextLeaf(idcommitment)
} }
@ -80,50 +54,10 @@ func (i RLNWrapper) GetRoot() ([]byte, error) {
return i.ffi.GetRoot() return i.ffi.GetRoot()
} }
func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
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)
} }
func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) bool {
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) {
return i.ffi.RecoverIDSecret(proof1, proof2)
}
func (i RLNWrapper) SetMetadata(metadata []byte) bool {
return i.ffi.SetMetadata(metadata)
}
func (i RLNWrapper) GetMetadata() ([]byte, error) {
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,54 +5,32 @@
package link package link
import ( import r "github.com/waku-org/go-zerokit-rln-x86_64/rln"
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, treeConfig []byte) (*RLNWrapper, error) { func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig) rln, err := r.NewWithParams(depth, wasm, zkey, verifKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func New(depth int, config []byte) (*RLNWrapper, error) { func NewWithFolder(depth int, resourcesFolderPath string) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config) rln, err := r.NewWithFolder(depth, resourcesFolderPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &RLNWrapper{ffi: rln}, nil return &RLNWrapper{ffi: rln}, nil
} }
func (i RLNWrapper) SetTree(treeHeight uint) bool {
return i.ffi.SetTree(treeHeight)
}
func (i RLNWrapper) InitTreeWithLeaves(idcommitments []byte) bool {
return i.ffi.InitTreeWithLeaves(idcommitments)
}
func (i RLNWrapper) KeyGen() []byte {
return i.ffi.KeyGen()
}
func (i RLNWrapper) SeededKeyGen(seed []byte) []byte {
return i.ffi.SeededKeyGen(seed)
}
func (i RLNWrapper) ExtendedKeyGen() []byte { func (i RLNWrapper) ExtendedKeyGen() []byte {
return i.ffi.ExtendedKeyGen() return i.ffi.ExtendedKeyGen()
} }
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) { func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input) return i.ffi.Hash(input)
} }
@ -61,10 +39,6 @@ func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
return i.ffi.PoseidonHash(input) return i.ffi.PoseidonHash(input)
} }
func (i RLNWrapper) SetLeaf(index uint, idcommitment []byte) bool {
return i.ffi.SetLeaf(index, idcommitment)
}
func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool { func (i RLNWrapper) SetNextLeaf(idcommitment []byte) bool {
return i.ffi.SetNextLeaf(idcommitment) return i.ffi.SetNextLeaf(idcommitment)
} }
@ -81,50 +55,10 @@ func (i RLNWrapper) GetRoot() ([]byte, error) {
return i.ffi.GetRoot() return i.ffi.GetRoot()
} }
func (i RLNWrapper) GetLeaf(index uint) ([]byte, error) {
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)
} }
func (i RLNWrapper) AtomicOperation(index uint, leaves []byte, indices []byte) bool {
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) {
return i.ffi.RecoverIDSecret(proof1, proof2)
}
func (i RLNWrapper) SetMetadata(metadata []byte) bool {
return i.ffi.SetMetadata(metadata)
}
func (i RLNWrapper) GetMetadata() ([]byte, error) {
return i.ffi.GetMetadata()
}
func (i RLNWrapper) Flush() bool {
return i.ffi.Flush()
}
func (i RLNWrapper) LeavesSet() uint {
return i.ffi.LeavesSet()
}

345
rln/resources/bindata.go Normal file

File diff suppressed because one or more lines are too long

3
rln/resources/doc.go Normal file
View File

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

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,119 @@
{
"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,49 +3,55 @@ 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) {
return NewWithConfig(DefaultTreeDepth, nil) wasm, err := resources.Asset("tree_height_20/rln.wasm")
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, treeConfig *TreeConfig) (*RLN, error) { func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error var err error
treeConfigBytes := []byte{} r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey)
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
} }
@ -53,21 +59,15 @@ func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, tree
return r, nil return r, nil
} }
// NewWithConfig generates an instance of RLN. An instance supports both zkSNARKs logics // NewRLNWithFolder 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 `deptk` indicates the depth of Merkle tree
func NewWithConfig(depth TreeDepth, treeConfig *TreeConfig) (*RLN, error) { // The parameter “
func NewRLNWithFolder(depth int, resourcesFolderPath string) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error var err error
configBytes, err := json.Marshal(config{ r.w, err = link.NewWithFolder(depth, resourcesFolderPath)
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
} }
@ -75,32 +75,19 @@ func NewWithConfig(depth TreeDepth, treeConfig *TreeConfig) (*RLN, error) {
return r, nil return r, nil
} }
func (r *RLN) SetTree(treeHeight uint) error { // MembershipKeyGen generates a IdentityCredential that can be used for the
success := r.w.SetTree(treeHeight) // registration into the rln membership contract. Returns an error if the key generation fails
if !success { func (r *RLN) MembershipKeyGen() (*IdentityCredential, error) {
return errors.New("could not set tree height") generatedKeys := r.w.ExtendedKeyGen()
if generatedKeys == nil {
return nil, errors.New("error in key generation")
} }
return nil
}
// Initialize merkle tree with a list of IDCommitments
func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
idCommBytes := serializeCommitments(idComms)
initSuccess := r.w.InitTreeWithLeaves(idCommBytes)
if !initSuccess {
return errors.New("could not init tree")
}
return nil
}
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,65 +102,15 @@ func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*Ident
return key, nil return key, nil
} }
// MembershipKeyGen generates a IdentityCredential that can be used for the
// registration into the rln membership contract. Returns an error if the key generation fails
// 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()
if generatedKeys == nil {
return nil, errors.New("error in key generation")
}
return toIdentityCredential(generatedKeys, userMessageLimit)
}
// SeededMembershipKeyGen generates a deterministic IdentityCredential using a seed
// that can be used for the registration into the rln membership contract.
// Returns an error if the key generation fails
// 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)
if generatedKeys == nil {
return nil, errors.New("error in key generation")
}
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)
@ -207,131 +144,65 @@ 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( func (r *RLN) GenerateProof(data []byte, key IdentityCredential, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) {
data []byte, input := serialize(key.IDSecretHash, index, epoch, data)
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
} }
expectedBytes := 288 if len(proofBytes) != 320 {
if len(proofBytes) != expectedBytes { return nil, errors.New("invalid proof generated")
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 // parse the proof as [ 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>]
proofOffset := 128 proofOffset := 128
rootOffset := proofOffset + 32 rootOffset := proofOffset + 32
externalNullifierOffset := rootOffset + 32 epochOffset := rootOffset + 32
shareXOffset := externalNullifierOffset + 32 shareXOffset := epochOffset + 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 externalNullifier Nullifier var epochR Epoch
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(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset]) copy(epochR[:], proofBytes[rootOffset:epochOffset])
copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset]) copy(shareX[:], proofBytes[epochOffset: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,
ExternalNullifier: externalNullifier, Epoch: epochR,
ShareX: shareX, ShareX: shareX,
ShareY: shareY, ShareY: shareY,
Nullifier: nullifier, Nullifier: nullifier,
}, nil RLNIdentifier: rlnIdentifier,
}
// 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
} }
@ -368,25 +239,11 @@ func serializeCommitments(commitments []IDCommitment) []byte {
return result return result
} }
func serializeIndices(indices []MembershipIndex) []byte {
var result []byte
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(indices)))
result = append(result, inputLen...)
for _, index := range indices {
result = binary.LittleEndian.AppendUint64(result, uint64(index))
}
return result
}
// proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ] // proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
// validRoots should contain a sequence of roots in the acceptable windows. // validRoots should contain a sequence of roots in the acceptable windows.
// As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped // As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped
func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool, error) { func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool, error) {
proofBytes := proof.serializeWithData(data) proofBytes := proof.serialize(data)
rootBytes := serialize32(roots) rootBytes := serialize32(roots)
res, err := r.w.VerifyWithRoots(proofBytes, rootBytes) res, err := r.w.VerifyWithRoots(proofBytes, rootBytes)
@ -394,68 +251,29 @@ func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool
return false, err return false, err
} }
return res, nil return bool(res), nil
} }
// RecoverIDSecret returns an IDSecret having obtained before two proofs // InsertMember adds the member to the tree
func (r *RLN) RecoverIDSecret(proof1 RateLimitProof, proof2 RateLimitProof) (IDSecretHash, error) { func (r *RLN) InsertMember(idComm IDCommitment) error {
proof1Bytes := proof1.serialize() insertionSuccess := r.w.SetNextLeaf(idComm[:])
proof2Bytes := proof2.serialize()
secret, err := r.w.RecoverIDSecret(proof1Bytes, proof2Bytes)
if err != nil {
return IDSecretHash{}, err
}
var result IDSecretHash
copy(result[:], secret)
return result, nil
}
// InsertMember adds the member to the tree. The leaf is made of
// the id commitment and the user message limit
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 {
idCommBytes := serializeCommitments(idComms) idCommBytes := serializeCommitments(idComms)
indicesBytes := serializeIndices(nil) insertionSuccess := r.w.SetLeavesFrom(index, idCommBytes)
insertionSuccess := r.w.AtomicOperation(index, idCommBytes, indicesBytes)
if !insertionSuccess { if !insertionSuccess {
return errors.New("could not insert members") return errors.New("could not insert members")
} }
return nil return nil
} }
// Insert a member in the tree at specified index
func (r *RLN) InsertMemberAt(index MembershipIndex, idComm IDCommitment) error {
insertionSuccess := r.w.SetLeaf(index, idComm[:])
if !insertionSuccess {
return errors.New("could not insert member")
}
return nil
}
// DeleteMember removes an IDCommitment key from the tree. The index // DeleteMember removes an IDCommitment key from the tree. The index
// parameter is the position of the id commitment key to be deleted from the tree. // parameter is the position of the id commitment key to be deleted from the tree.
// The deleted id commitment key is replaced with a zero leaf // The deleted id commitment key is replaced with a zero leaf
@ -467,17 +285,6 @@ func (r *RLN) DeleteMember(index MembershipIndex) error {
return nil return nil
} }
// Delete multiple members
func (r *RLN) DeleteMembers(indices []MembershipIndex) error {
idCommBytes := serializeCommitments(nil)
indicesBytes := serializeIndices(indices)
insertionSuccess := r.w.AtomicOperation(0, idCommBytes, indicesBytes)
if !insertionSuccess {
return errors.New("could not insert members")
}
return nil
}
// GetMerkleRoot reads the Merkle Tree root after insertion // GetMerkleRoot reads the Merkle Tree root after insertion
func (r *RLN) GetMerkleRoot() (MerkleNode, error) { func (r *RLN) GetMerkleRoot() (MerkleNode, error) {
b, err := r.w.GetRoot() b, err := r.w.GetRoot()
@ -495,48 +302,10 @@ func (r *RLN) GetMerkleRoot() (MerkleNode, error) {
return result, nil return result, nil
} }
// GetLeaf reads the value stored at some index in the Merkle Tree
func (r *RLN) GetLeaf(index MembershipIndex) (IDCommitment, error) {
b, err := r.w.GetLeaf(index)
if err != nil {
return IDCommitment{}, err
}
if len(b) != 32 {
return IDCommitment{}, errors.New("wrong output size")
}
var result IDCommitment
copy(result[:], b)
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 []IdentityCredential) error { func (r *RLN) AddAll(list []IDCommitment) error {
for _, member := range list { for _, member := range list {
if err := r.InsertMember(member.IDCommitment, member.UserMessageLimit); err != nil { if err := r.InsertMember(member); err != nil {
return err return err
} }
} }
@ -550,8 +319,11 @@ func CalcMerkleRoot(list []IDCommitment) (MerkleNode, error) {
return MerkleNode{}, err return MerkleNode{}, err
} }
if err := rln.InsertMembers(0, list); err != nil { // create a Merkle tree
return MerkleNode{}, err for _, c := range list {
if err := rln.InsertMember(c); err != nil {
return MerkleNode{}, err
}
} }
return rln.GetMerkleRoot() return rln.GetMerkleRoot()
@ -578,7 +350,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, keypair.UserMessageLimit); err != nil { if err := rln.InsertMember(keypair.IDCommitment); err != nil {
return nil, MerkleNode{}, err return nil, MerkleNode{}, err
} }
} }
@ -590,42 +362,3 @@ func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
return output, root, nil return output, root, nil
} }
// SetMetadata stores serialized data
func (r *RLN) SetMetadata(metadata []byte) error {
success := r.w.SetMetadata(metadata)
if !success {
return errors.New("could not set metadata")
}
return nil
}
// GetMetadata returns the stored serialized metadata
func (r *RLN) GetMetadata() ([]byte, error) {
return r.w.GetMetadata()
}
// AtomicOperation can be used to insert and remove elements into the merkle tree
func (r *RLN) AtomicOperation(index MembershipIndex, idCommsToInsert []IDCommitment, indicesToRemove []MembershipIndex) error {
idCommBytes := serializeCommitments(idCommsToInsert)
indicesBytes := serializeIndices(indicesToRemove)
execSuccess := r.w.AtomicOperation(index, idCommBytes, indicesBytes)
if !execSuccess {
return errors.New("could not execute atomic_operation")
}
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,23 +17,6 @@ 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)
@ -76,69 +59,25 @@ func (s *RLNSuite) TestInsertMember() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit) err = rln.InsertMember(keypair.IDCommitment)
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)
var commitments []IDCommitment keypair, err := rln.MembershipKeyGen()
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)
numLeaves := rln.LeavesSet() err = rln.InsertMembers(0, []IDCommitment{keypair.IDCommitment})
s.Equal(uint(10), numLeaves) s.NoError(err)
} }
func (s *RLNSuite) TestRemoveMember() { func (s *RLNSuite) TestRemoveMember() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
keypair, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err)
err = rln.DeleteMember(MembershipIndex(0)) err = rln.DeleteMember(MembershipIndex(0))
s.NoError(err) s.NoError(err)
} }
@ -154,7 +93,7 @@ func (s *RLNSuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit) err = rln.InsertMember(keypair.IDCommitment)
s.NoError(err) s.NoError(err)
// read the Merkle Tree root after insertion // read the Merkle Tree root after insertion
@ -238,63 +177,28 @@ 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)
// allowed messages per epoch of the membership memKeys, err := rln.MembershipKeyGen()
userMessageLimit := uint32(10)
//peer's index in the Merkle Tree
index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
//peer's index in the Merkle Tree
index := 5
// Create a Merkle tree with random members // Create a Merkle tree with random members
for i := uint(0); i < 10; i++ { for i := 0; i < 10; i++ {
if i == index { if i == index {
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit) // insert the current peer's pk
err = rln.InsertMember(memKeys.IDCommitment)
s.NoError(err) s.NoError(err)
} else { } else {
// create a new key pair // create a new key pair
memberKeys, err := rln.MembershipKeyGen(userMessageLimit) memberKeys, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit) err = rln.InsertMember(memberKeys.IDCommitment)
s.NoError(err) s.NoError(err)
} }
} }
@ -303,75 +207,24 @@ func (s *RLNSuite) TestValidProof() {
msg := []byte("Hello") msg := []byte("Hello")
// prepare the epoch // prepare the epoch
var epoch Epoch = SerializeUint32(1000) var epoch Epoch
// generate multiple valid proofs for the same epoch // generate proof
for i := uint32(0); i < userMessageLimit; i++ { proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch)
// 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)
// allowed messages per epoch of the membership // verify the proof
userMessageLimit := uint32(10) verified, err := rln.Verify(msg, *proofRes)
s.NoError(err)
s.True(verified)
//peer's index in the Merkle Tree // verify with roots
index := uint(5) root, err := rln.GetMerkleRoot()
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
// Create a Merkle tree with random members verified, err = rln.Verify(msg, *proofRes, root)
for i := uint(0); i < 10; i++ { s.NoError(err)
if i == index { s.True(verified)
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() {
@ -388,14 +241,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, memKeys.UserMessageLimit) err := rln.InsertMember(memKeys.IDCommitment)
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, memberKeys.UserMessageLimit) err = rln.InsertMember(memberKeys.IDCommitment)
s.NoError(err) s.NoError(err)
} }
} }
@ -411,11 +264,8 @@ 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, messageId) proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch)
s.NoError(err) s.NoError(err)
// verify the proof (should not be verified) // verify the proof (should not be verified)
@ -424,232 +274,6 @@ 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,37 +1,20 @@
package rln package rln
import ( import "encoding/binary"
"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
// format taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L747 // this conversion is used in the proofGen function
// [identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ] // the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146
func serialize( // [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
idKey IDSecretHash, func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg []byte) []byte {
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, userMessageLimitByte[:]...) output = append(output, epoch[:]...)
output = append(output, messageIdByte[:]...)
output = append(output, externalNullifier[:]...)
output = append(output, lenPrefMsg...) output = append(output, lenPrefMsg...)
return output return output
@ -40,111 +23,16 @@ func serialize(
// 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> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal<var> ] // [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ]
func (r RateLimitProof) serializeWithData(data []byte) []byte { func (r RateLimitProof) serialize(data []byte) []byte {
lenPrefMsg := appendLength(data) lenPrefMsg := appendLength(data)
proofBytes := r.serialize()
proofBytes = append(proofBytes, lenPrefMsg...)
return proofBytes
}
// serialize converts a RateLimitProof to a byte seq
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
func (r RateLimitProof) serialize() []byte {
proofBytes := append(r.Proof[:], r.MerkleRoot[:]...) proofBytes := append(r.Proof[:], r.MerkleRoot[:]...)
proofBytes = append(proofBytes, r.ExternalNullifier[:]...) proofBytes = append(proofBytes, r.Epoch[:]...)
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[:]...)
proofBytes = append(proofBytes, lenPrefMsg...)
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
}

View File

@ -1,65 +0,0 @@
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,7 +3,6 @@ package rln
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json"
"time" "time"
) )
@ -36,19 +35,8 @@ 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 {
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
@ -57,7 +45,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`
ExternalNullifier Nullifier `json:"external_nullifier"` Epoch Epoch `json:"epoch"`
// 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
@ -66,68 +54,8 @@ 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
@ -172,7 +100,9 @@ func init() {
// STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above // STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above
// only identity commitments are used for the Merkle tree construction // only identity commitments are used for the Merkle tree construction
// 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 = "25caa6e82a7476394b0ad5bfbca174a0a842479e70eaaeee14fa8096e49072ca"
const EPOCH_UNIT_SECONDS = uint64(10) // the rln-relay epoch length in seconds
type Epoch [32]byte type Epoch [32]byte
@ -193,13 +123,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, epochSize uint64) Epoch { func CalcEpoch(t time.Time) Epoch {
return ToEpoch(uint64(t.Unix()) / epochSize) return ToEpoch(uint64(t.Unix()) / EPOCH_UNIT_SECONDS)
} }
// GetCurrentEpoch gets the current rln Epoch time // GetCurrentEpoch gets the current rln Epoch time
func GetCurrentEpoch(epochSize uint64) Epoch { func GetCurrentEpoch() Epoch {
return CalcEpoch(time.Now(), epochSize) return CalcEpoch(time.Now())
} }
// 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`
@ -209,6 +139,6 @@ func Diff(e1, e2 Epoch) int64 {
return int64(epoch1) - int64(epoch2) return int64(epoch1) - int64(epoch2)
} }
func (e Epoch) Time(epochSize uint64) time.Time { func (e Epoch) Time() time.Time {
return time.Unix(int64(e.Uint64()*epochSize), 0) return time.Unix(int64(e.Uint64()*EPOCH_UNIT_SECONDS), 0)
} }

View File

@ -1,14 +1,7 @@
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) {
@ -62,14 +55,6 @@ 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)
@ -77,100 +62,9 @@ func ToBytes32LE(hexStr string) ([32]byte, error) {
return [32]byte{}, err return [32]byte{}, err
} }
bLen := len(b) for i := 0; i < len(b)/2; i++ {
for i := 0; i < bLen/2; i++ { b[i], b[len(b)-i-1] = b[len(b)-i-1], b[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
}

View File

@ -1,53 +0,0 @@
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)
}