Compare commits

..

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

20 changed files with 673 additions and 1289 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-20230803113401-9a7ef94d120e
github.com/stretchr/testify v1.8.4 github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230801152407-8101ff87ee0a
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-20230801140722-0a4e68d0b8f5
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
) )

53
go.sum
View File

@ -1,53 +1,26 @@
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-20230803113401-9a7ef94d120e h1:Ad0rJod5F1FuYCJ8SUB/bQZsQwirNHQRE0IcaVloxZo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230803113401-9a7ef94d120e/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-arm v0.0.0-20230801152407-8101ff87ee0a h1:10cre+P76QvnLeyeCVAM8WDbUCri/y5xY3LtwI9Y5DE=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= 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-20240523161310-d005fe7ba59c h1:/eGH8EAt5/zGfNRBQ0nJMrfZDeXRSJrm8E8uCPlsC3A= 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/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= 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-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/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,24 +5,22 @@
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
} }
@ -85,18 +83,10 @@ 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)
} }
@ -105,10 +95,6 @@ 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)
} }
@ -120,11 +106,3 @@ 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,24 +4,22 @@
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
} }
@ -84,18 +82,10 @@ 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)
} }
@ -104,10 +94,6 @@ 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)
} }
@ -119,11 +105,3 @@ 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,24 +5,22 @@
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
} }
@ -85,18 +83,10 @@ 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)
} }
@ -105,10 +95,6 @@ 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)
} }
@ -120,11 +106,3 @@ 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()
}

369
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,41 @@ 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
// 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
func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig *TreeConfig) (*RLN, error) {
r := &RLN{}
var err error
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) 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 { if err != nil {
return nil, err return nil, err
} }
@ -53,21 +45,29 @@ 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 // 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 NewWithConfig(depth TreeDepth, 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
configBytes, err := json.Marshal(config{ r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey)
ResourcesFolder: getResourcesFolder(depth),
TreeConfig: treeConfig,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.w, err = link.New(int(depth), configBytes) return r, nil
}
// NewRLNWithFolder 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
// The parameter “
func NewRLNWithFolder(depth int, resourcesFolderPath string) (*RLN, error) {
r := &RLN{}
var err error
r.w, err = link.NewWithFolder(depth, resourcesFolderPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,14 +93,12 @@ func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
return nil return nil
} }
func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*IdentityCredential, error) { func toIdentityCredential(generatedKeys []byte) (*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 {
@ -117,63 +115,34 @@ func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*Ident
// 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
// Accepts an optional parameter that sets the user message limit which defaults func (r *RLN) MembershipKeyGen() (*IdentityCredential, error) {
// 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, userMessageLimit) return toIdentityCredential(generatedKeys)
} }
// 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
// Accepts an optional parameter that sets the user message limit which defaults func (r *RLN) SeededMembershipKeyGen(seed []byte) (*IdentityCredential, error) {
// 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, userMessageLimit) return toIdentityCredential(generatedKeys)
} }
// 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 +176,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
} }
@ -394,7 +297,7 @@ 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 // RecoverIDSecret returns an IDSecret having obtained before two proofs
@ -410,31 +313,15 @@ func (r *RLN) RecoverIDSecret(proof1 RateLimitProof, proof2 RateLimitProof) (IDS
return result, nil return result, nil
} }
// InsertMember adds the member to the tree. The leaf is made of // InsertMember adds the member to the tree
// the id commitment and the user message limit func (r *RLN) InsertMember(idComm IDCommitment) error {
func (r *RLN) InsertMember(idComm IDCommitment, userMessageLimit uint32) error { insertionSuccess := r.w.SetNextLeaf(idComm[:])
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 {
@ -512,31 +399,10 @@ 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 []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
} }
} }
@ -578,7 +444,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
} }
} }
@ -615,17 +481,3 @@ 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,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,57 +59,19 @@ 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() {
@ -136,7 +81,7 @@ func (s *RLNSuite) TestRemoveMember() {
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)
err = rln.DeleteMember(MembershipIndex(0)) err = rln.DeleteMember(MembershipIndex(0))
@ -154,7 +99,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,64 +183,37 @@ 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) s.NoError(err)
//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 {
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit) // insert the current peer's pk
s.NoError(err) err = rln.InsertMember(memKeys.IDCommitment)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit) fifthIndexLeaf, err := rln.GetLeaf(index)
s.NoError(err) s.NoError(err)
s.Equal(memKeys.IDCommitment, fifthIndexLeaf)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment)
s.NoError(err)
leaf, err := rln.GetLeaf(i)
s.NoError(err)
s.Equal(memberKeys.IDCommitment, leaf)
} }
} }
@ -303,75 +221,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 +255,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 +278,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 +288,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,7 +23,7 @@ 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) serializeWithData(data []byte) []byte {
lenPrefMsg := appendLength(data) lenPrefMsg := appendLength(data)
proofBytes := r.serialize() proofBytes := r.serialize()
@ -49,102 +32,13 @@ 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> | external_nullifier<32> | x<32> | y<32> | nullifier<32>] // [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<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.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[:]...)
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,12 @@ 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[:]) && 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[:])
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 +49,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 +58,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
@ -174,6 +106,8 @@ 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 {
@ -193,13 +127,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 +143,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)
}