Compare commits

...

30 Commits

Author SHA1 Message Date
Alvaro Revuelta
a8e8aab76c
Remove hardcoded epoch size (#24) 2024-06-14 12:20:49 +02:00
Alvaro Revuelta
88462cf654
Add InsertRawLeaf (#23)
Co-authored-by: richΛrd <info@richardramos.me>
2024-05-31 07:11:54 +02:00
Alvaro Revuelta
84d12e61d9
Custom witness proof with RLN v2 (#22) 2024-05-30 14:53:54 +02:00
Alvaro Revuelta
54bb48f178
Update to RLN v2 (#21) 2024-05-24 10:17:29 +02:00
Alvaro Revuelta
14960f3aff
Add GenerateRLNProofWithWitness (#19) 2024-01-24 16:31:36 +01:00
Alvaro Revuelta
7e086e8f89
Add GetMerkleProof (#17) 2024-01-16 16:33:45 +01:00
Alvaro Revuelta
06e6fa3fd1
Add CI for tests (#18) 2024-01-16 15:31:15 +01:00
Prem Chaitanya Prathi
fa738c0bdf fix: make rln rate limit spec compliant 2024-01-02 10:52:50 -04:00
Richard Ramos
e0f344a581
chore: update README.md 2023-12-15 08:50:18 -04:00
Richard Ramos
4b71a4fcf1
chore: update README.md with instructions on how to update zerokit 2023-12-15 08:34:52 -04:00
Richard Ramos
d284a3d8f2
chore: add reference to library folders for go mod vendor 2023-09-16 13:32:59 -04:00
Richard Ramos
e0ebce7c29
feat: upgrade to zerokit 0.3.4 2023-09-14 19:40:36 -04:00
Richard Ramos
ca686a02e8
feat: upgrade to zerokit 0.3.2 2023-09-05 17:46:45 -04:00
Richard Ramos
a706089284
chore: use flush_every_ms and change datatype to time.Duration 2023-08-23 11:08:36 -04:00
Richard Ramos
8167006b94
fix: invalid apple commit 2023-08-21 12:37:54 -04:00
Richard Ramos
1ccba817b5
chore: add Bytes32ToBigInt 2023-08-17 18:19:53 -04:00
Richard Ramos
3b5c0bbefb
chore: add BigIntToBytes32 utils function 2023-08-14 07:57:45 -04:00
Richard Ramos
47b8b17401 refactor: only expose tree config 2023-08-11 08:46:33 -04:00
Richard Ramos
fd3fa1222b refactor: remove embedded resource and add depth and mode enums 2023-08-11 08:46:33 -04:00
Richard Ramos
655973b243 chore: allow passing configs when instantiating RLN 2023-08-07 15:54:39 -04:00
Richard Ramos
ea89e5d7ee
fix: missing functions in macos 2023-08-03 07:37:01 -04:00
Richard Ramos
0cefd63734
chore: expose atomic_operation 2023-08-01 11:56:27 -04:00
Richard Ramos
75151c168b feat: expose additional zerokit functions 2023-08-01 11:35:48 -04:00
Richard Ramos
df71229905 feat: getLeaf 2023-08-01 09:22:02 -04:00
Richard Ramos
86b06ba440 feat: atomic_write 2023-05-18 16:27:29 -04:00
Richard Ramos
33aab2e6e4
feat: compare identity credentials 2023-04-10 11:06:09 -04:00
Richard Ramos
810b73f03c
fix: convert ExtractMetadata to method 2023-04-04 13:44:30 -04:00
Richard Ramos
deaab0718c chore: update static keys 2023-04-03 17:39:19 -04:00
RichΛrd
f3f2c16093
refactor: split into submodules
* chore: remove zerokit submodule
* refactor: split go-zerokit-rln into submodules
2023-04-01 08:25:39 -04:00
Richard Ramos
eaf62c1713 chore: upgrade zerokit 2023-03-31 14:19:48 -04:00
36 changed files with 1882 additions and 1029 deletions

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

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

4
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "zerokit"]
path = zerokit
url = git@github.com:vacp2p/zerokit.git

View File

@ -1,12 +0,0 @@
.PHONY: rlnlib
SHELL := bash # the shell used internally by Make
GOBIN ?= $(shell which go)
rlnlib:
scripts/build.sh
cd zerokit/rln && cbindgen --config ../../cbindgen.toml --crate rln --output ../../rln/librln.h --lang c
test:
go test ./... -count 1 -v

View File

@ -2,13 +2,81 @@
Go wrappers for [zerokit's RLN](https://github.com/vacp2p/zerokit) Go wrappers for [zerokit's RLN](https://github.com/vacp2p/zerokit)
### Building this library
```
git clone https://github.com/waku-org/go-zerokit-rln ### Updating vacp2p/zerokit
cd go-zerokit-rln
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 init
git submodule update --recursive git submodule update --recursive
cd zerokit
git pull
git checkout ${ZEROKIT_COMMIT}
cd ..
make make
git add zerokit
git add libs/*/librln.a
git commit -m "chore: bump zerokit"
git push
``` ```
To generate smaller static libraries, before `make`, edit `./zerokit/rln/Cargo.toml` and use `branch = "no-ethers-core"` for `ark-circom` 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
```

18
go.mod
View File

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

47
go.sum
View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,75 +0,0 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define TEST_PARAMETERS_INDEX 2
/**
* The RLN object.
*
* It implements the methods required to update the internal Merkle Tree, generate and verify RLN ZK proofs.
*
* I/O is mostly done using writers and readers implementing `std::io::Write` and `std::io::Read`, respectively.
*/
typedef struct RLN RLN;
/**
* Buffer struct is taken from
* <https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs>
*
* Also heavily inspired by <https://github.com/kilic/rln/blob/master/src/ffi.rs>
*/
typedef struct Buffer {
const uint8_t *ptr;
uintptr_t len;
} Buffer;
bool new(uintptr_t tree_height, const struct Buffer *input_buffer, struct RLN **ctx);
bool new_with_params(uintptr_t tree_height,
const struct Buffer *circom_buffer,
const struct Buffer *zkey_buffer,
const struct Buffer *vk_buffer,
struct RLN **ctx);
bool set_tree(struct RLN *ctx, uintptr_t tree_height);
bool delete_leaf(struct RLN *ctx, uintptr_t index);
bool set_leaf(struct RLN *ctx, uintptr_t index, const struct Buffer *input_buffer);
bool set_next_leaf(struct RLN *ctx, const struct Buffer *input_buffer);
bool set_leaves_from(struct RLN *ctx, uintptr_t index, const struct Buffer *input_buffer);
bool init_tree_with_leaves(struct RLN *ctx, const struct Buffer *input_buffer);
bool get_root(const struct RLN *ctx, struct Buffer *output_buffer);
bool get_proof(const struct RLN *ctx, uintptr_t index, struct Buffer *output_buffer);
bool prove(struct RLN *ctx, const struct Buffer *input_buffer, struct Buffer *output_buffer);
bool verify(const struct RLN *ctx, const struct Buffer *proof_buffer, bool *proof_is_valid_ptr);
bool generate_rln_proof(struct RLN *ctx,
const struct Buffer *input_buffer,
struct Buffer *output_buffer);
bool verify_rln_proof(const struct RLN *ctx,
const struct Buffer *proof_buffer,
bool *proof_is_valid_ptr);
bool verify_with_roots(const struct RLN *ctx,
const struct Buffer *proof_buffer,
const struct Buffer *roots_buffer,
bool *proof_is_valid_ptr);
bool key_gen(const struct RLN *ctx, struct Buffer *output_buffer);
bool seeded_key_gen(const struct RLN *ctx,
const struct Buffer *input_buffer,
struct Buffer *output_buffer);
bool hash(struct RLN *ctx, const struct Buffer *input_buffer, struct Buffer *output_buffer);

View File

@ -1,16 +0,0 @@
package rln
/*
#cgo LDFLAGS:-lrln -ldl -lm
#cgo linux,arm LDFLAGS:-L${SRCDIR}/../libs/armv7-linux-androideabi
#cgo linux,arm64 LDFLAGS:-L${SRCDIR}/../libs/aarch64-unknown-linux-gnu
#cgo linux,amd64,musl,!android LDFLAGS:-L${SRCDIR}/../libs/x86_64-unknown-linux-musl
#cgo linux,amd64,!musl,!android LDFLAGS:-L${SRCDIR}/../libs/x86_64-unknown-linux-gnu
#cgo linux,386 LDFLAGS:-L${SRCDIR}/../libs/i686-unknown-linux-gnu
#cgo windows,386 LDFLAGS:-L${SRCDIR}/../libs/i686-pc-windows-gnu -lrln -lm -lws2_32 -luserenv
#cgo windows,amd64 LDFLAGS:-L${SRCDIR}/../libs/x86_64-pc-windows-gnu -lrln -lm -lws2_32 -luserenv
#cgo darwin,386,!ios LDFLAGS:-L${SRCDIR}/../libs/i686-apple-darwin
#cgo darwin,arm64,!ios LDFLAGS:-L${SRCDIR}/../libs/aarch64-apple-darwin
#cgo darwin,amd64,!ios LDFLAGS:-L${SRCDIR}/../libs/x86_64-apple-darwin
*/
import "C"

130
rln/link/apple.go Normal file
View File

@ -0,0 +1,130 @@
//go:build (386 || arm64 || amd64) && darwin && !ios
// +build 386 arm64 amd64
// +build darwin
// +build !ios
package link
import (
r "github.com/waku-org/go-zerokit-rln-apple/rln"
)
type RLNWrapper struct {
ffi *r.RLN
}
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil {
return nil, err
}
return &RLNWrapper{ffi: rln}, nil
}
func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config)
if err != nil {
return nil, err
}
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 {
return i.ffi.ExtendedKeyGen()
}
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input)
}
func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
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 {
return i.ffi.SetNextLeaf(idcommitment)
}
func (i RLNWrapper) SetLeavesFrom(index uint, idcommitments []byte) bool {
return i.ffi.SetLeavesFrom(index, idcommitments)
}
func (i RLNWrapper) DeleteLeaf(index uint) bool {
return i.ffi.DeleteLeaf(index)
}
func (i RLNWrapper) GetRoot() ([]byte, error) {
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) {
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) {
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()
}

129
rln/link/arm.go Normal file
View File

@ -0,0 +1,129 @@
//go:build (arm || arm64) && linux
// +build arm arm64
// +build linux
package link
import (
r "github.com/waku-org/go-zerokit-rln-arm/rln"
)
type RLNWrapper struct {
ffi *r.RLN
}
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil {
return nil, err
}
return &RLNWrapper{ffi: rln}, nil
}
func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config)
if err != nil {
return nil, err
}
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 {
return i.ffi.ExtendedKeyGen()
}
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input)
}
func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
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 {
return i.ffi.SetNextLeaf(idcommitment)
}
func (i RLNWrapper) SetLeavesFrom(index uint, idcommitments []byte) bool {
return i.ffi.SetLeavesFrom(index, idcommitments)
}
func (i RLNWrapper) DeleteLeaf(index uint) bool {
return i.ffi.DeleteLeaf(index)
}
func (i RLNWrapper) GetRoot() ([]byte, error) {
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) {
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) {
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()
}

130
rln/link/x86_64.go Normal file
View File

@ -0,0 +1,130 @@
//go:build (linux || windows) && amd64 && !android
// +build linux windows
// +build amd64
// +build !android
package link
import (
r "github.com/waku-org/go-zerokit-rln-x86_64/rln"
)
type RLNWrapper struct {
ffi *r.RLN
}
func NewWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig []byte) (*RLNWrapper, error) {
rln, err := r.NewWithParams(depth, wasm, zkey, verifKey, treeConfig)
if err != nil {
return nil, err
}
return &RLNWrapper{ffi: rln}, nil
}
func New(depth int, config []byte) (*RLNWrapper, error) {
rln, err := r.New(uint(depth), config)
if err != nil {
return nil, err
}
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 {
return i.ffi.ExtendedKeyGen()
}
func (i RLNWrapper) ExtendedSeededKeyGen(seed []byte) []byte {
return i.ffi.ExtendedSeededKeyGen(seed)
}
func (i RLNWrapper) Hash(input []byte) ([]byte, error) {
return i.ffi.Hash(input)
}
func (i RLNWrapper) PoseidonHash(input []byte) ([]byte, error) {
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 {
return i.ffi.SetNextLeaf(idcommitment)
}
func (i RLNWrapper) SetLeavesFrom(index uint, idcommitments []byte) bool {
return i.ffi.SetLeavesFrom(index, idcommitments)
}
func (i RLNWrapper) DeleteLeaf(index uint) bool {
return i.ffi.DeleteLeaf(index)
}
func (i RLNWrapper) GetRoot() ([]byte, error) {
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) {
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) {
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()
}

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,119 +0,0 @@
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 6,
"vk_alpha_1": [
"1805378556360488226980822394597799963030511477964155500103132920745199284516",
"11990395240534218699464972016456017378439762088320057798320175886595281336136",
"1"
],
"vk_beta_2": [
[
"11031529986141021025408838211017932346992429731488270384177563837022796743627",
"16042159910707312759082561183373181639420894978640710177581040523252926273854"
],
[
"20112698439519222240302944148895052359035104222313380895334495118294612255131",
"19441583024670359810872018179190533814486480928824742448673677460151702019379"
],
[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_delta_2": [
[
"1948496782571164085469528023647105317580208688174386157591917599801657832035",
"20445814069256658101339037520922621162739470138213615104905368409238414511981"
],
[
"10024680869920840984813249386422727863826862577760330492647062850849851925340",
"10512156247842686783409460795717734694774542185222602679117887145206209285142"
],
[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"5151991366823434428398919091000210787450832786814248297320989361921939794156",
"15735191313289001022885148627913534790382722933676436876510746491415970766821"
],
[
"3387907257437913904447588318761906430938415556102110876587455322225272831272",
"1998779853452712881084781956683721603875246565720647583735935725110674288056"
],
[
"14280074182991498185075387990446437410077692353432005297922275464876153151820",
"17092408446352310039633488224969232803092763095456307462247653153107223117633"
]
],
[
[
"4359046709531668109201634396816565829237358165496082832279660960675584351266",
"4511888308846208349307186938266411423935335853916317436093178288331845821336"
],
[
"11429499807090785857812316277335883295048773373068683863667725283965356423273",
"16232274853200678548795010078253506586114563833318973594428907292096178657392"
],
[
"18068999605870933925311275504102553573815570223888590384919752303726860800970",
"17309569111965782732372130116757295842160193489132771344011460471298173784984"
]
]
],
"IC": [
[
"18693301901828818437917730940595978397160482710354161265484535387752523310572",
"17985273354976640088538673802000794244421192643855111089693820179790551470769",
"1"
],
[
"21164641723988537620541455173278629777250883365474191521194244273980931825942",
"998385854410718613441067082771678946155853656328717326195057262123686425518",
"1"
],
[
"21666968581672145768705229094968410656430989593283335488162701230986314747515",
"17996457608540683483506630273632100555125353447506062045735279661096094677264",
"1"
],
[
"20137761979695192602424300886442379728165712610493092740175904438282083668117",
"19184814924890679891263780109959113289320127263583260218200636509492157834679",
"1"
],
[
"10943171273393803842589314082509655332154393332394322726077270895078286354146",
"10872472035685319847811233167729172672344935625121511932198535224727331126439",
"1"
],
[
"13049169779481227658517545034348883391527506091990880778783387628208561946597",
"10083689369261379027228809473568899816311684698866922944902456565434209079955",
"1"
],
[
"19633516378466409167014413361365552102431118630694133723053441455184566611083",
"8059525100726933978719058611146131904598011633549012007359165766216730722269",
"1"
]
]
}

View File

@ -1,121 +1,163 @@
package rln package rln
/*
#include "./librln.h"
*/
import "C" import "C"
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json"
"errors" "errors"
"unsafe" "fmt"
"github.com/waku-org/go-zerokit-rln/rln/resources" "github.com/waku-org/go-zerokit-rln/rln/link"
) )
// 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 {
ptr *C.RLN 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
wasmBuffer := toCBufferPtr(wasm)
zkeyBuffer := toCBufferPtr(zkey)
verifKeyBuffer := toCBufferPtr(verifKey)
if !bool(C.new_with_params(C.uintptr_t(depth), wasmBuffer, zkeyBuffer, verifKeyBuffer, &r.ptr)) {
return nil, errors.New("failed to initialize")
}
return r, nil
} }
// NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics // NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree // and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree
func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLN, error) { func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig *TreeConfig) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error
wasmBuffer := toCBufferPtr(wasm) treeConfigBytes := []byte{}
zkeyBuffer := toCBufferPtr(zkey) if treeConfig != nil {
verifKeyBuffer := toCBufferPtr(verifKey) treeConfigBytes, err = json.Marshal(treeConfig)
if err != nil {
return nil, err
}
}
if !bool(C.new_with_params(C.uintptr_t(depth), wasmBuffer, zkeyBuffer, verifKeyBuffer, &r.ptr)) { r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey, treeConfigBytes)
return nil, errors.New("failed to initialize") if err != nil {
return nil, err
} }
return r, nil return r, nil
} }
// NewRLNWithFolder generates an instance of RLN. An instance supports both zkSNARKs logics // NewWithConfig generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `deptk` indicates the depth of Merkle tree // and Merkle tree data structure and operations. The parameter `depth` indicates the depth of Merkle tree
// The parameter “ func NewWithConfig(depth TreeDepth, treeConfig *TreeConfig) (*RLN, error) {
func NewRLNWithFolder(depth int, resourcesFolderPath string) (*RLN, error) {
r := &RLN{} r := &RLN{}
var err error
pathBuffer := toCBufferPtr([]byte(resourcesFolderPath)) configBytes, err := json.Marshal(config{
ResourcesFolder: getResourcesFolder(depth),
TreeConfig: treeConfig,
})
if err != nil {
return nil, err
}
if !bool(C.new(C.uintptr_t(depth), pathBuffer, &r.ptr)) { r.w, err = link.New(int(depth), configBytes)
return nil, errors.New("failed to initialize") if err != nil {
return nil, err
} }
return r, nil return r, nil
} }
func toCBufferPtr(input []byte) *C.Buffer { func (r *RLN) SetTree(treeHeight uint) error {
buf := toBuffer(input) success := r.w.SetTree(treeHeight)
if !success {
size := int(unsafe.Sizeof(buf)) return errors.New("could not set tree height")
in := (*C.Buffer)(C.malloc(C.size_t(size))) }
*in = buf return nil
return in
} }
// MembershipKeyGen generates a MembershipKeyPair that can be used for the registration into the rln membership contract // Initialize merkle tree with a list of IDCommitments
func (r *RLN) MembershipKeyGen() (*MembershipKeyPair, error) { func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
buffer := toBuffer([]byte{}) idCommBytes := serializeCommitments(idComms)
if !bool(C.key_gen(r.ptr, &buffer)) { initSuccess := r.w.InitTreeWithLeaves(idCommBytes)
return nil, errors.New("error in key generation") 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{
IDTrapdoor: [32]byte{},
IDNullifier: [32]byte{},
IDSecretHash: [32]byte{},
IDCommitment: [32]byte{},
UserMessageLimit: userMessageLimit,
} }
key := &MembershipKeyPair{ if len(generatedKeys) != 32*4 {
IDKey: [32]byte{}, return nil, errors.New("generated keys are of invalid length")
IDCommitment: [32]byte{},
} }
// the public and secret keys together are 64 bytes copy(key.IDTrapdoor[:], generatedKeys[:32])
generatedKeys := C.GoBytes(unsafe.Pointer(buffer.ptr), C.int(buffer.len)) copy(key.IDNullifier[:], generatedKeys[32:64])
if len(generatedKeys) != 64 { copy(key.IDSecretHash[:], generatedKeys[64:96])
return nil, errors.New("the generated keys are invalid") copy(key.IDCommitment[:], generatedKeys[96:128])
}
copy(key.IDKey[:], generatedKeys[:32])
copy(key.IDCommitment[:], generatedKeys[32:64])
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 {
@ -124,40 +166,40 @@ func appendLength(input []byte) []byte {
return append(inputLen, input...) return append(inputLen, input...)
} }
// toBuffer converts the input to a buffer object that is used to communicate data with the rln lib // Similar to appendLength but for 32 byte values. The length that is prepended is
func toBuffer(data []byte) C.Buffer { // the length of elements that are 32 bytes long each
dataPtr, dataLen := sliceToPtr(data) func appendLength32(input []byte) []byte {
return C.Buffer{ inputLen := make([]byte, 8)
ptr: dataPtr, binary.LittleEndian.PutUint64(inputLen, uint64(len(input)/32))
len: C.uintptr_t(dataLen), return append(inputLen, input...)
}
} }
func sliceToPtr(slice []byte) (*C.uchar, C.int) { func (r *RLN) Sha256(data []byte) (MerkleNode, error) {
if len(slice) == 0 {
return nil, 0
} else {
return (*C.uchar)(unsafe.Pointer(&slice[0])), C.int(len(slice))
}
}
// Hash hashes the plain text supplied in inputs_buffer and then maps it to a field element
// this proc is used to map arbitrary signals to field element for the sake of proof generation
// inputs holds the hash input as a byte slice, the output slice will contain a 32 byte slice
func (r *RLN) Hash(data []byte) (MerkleNode, error) {
// a thin layer on top of the Nim wrapper of the Poseidon hasher
lenPrefData := appendLength(data) lenPrefData := appendLength(data)
hashInputBuffer := toCBufferPtr(lenPrefData) b, err := r.w.Hash(lenPrefData)
if err != nil {
var output []byte return MerkleNode{}, err
out := toBuffer(output)
if !bool(C.hash(r.ptr, hashInputBuffer, &out)) {
return MerkleNode{}, errors.New("failed to hash")
} }
b := C.GoBytes(unsafe.Pointer(out.ptr), C.int(out.len)) var result MerkleNode
copy(result[:], b)
return result, nil
}
func (r *RLN) Poseidon(input ...[]byte) (MerkleNode, error) {
data := serializeSlice(input)
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(input)))
lenPrefData := append(inputLen, data...)
b, err := r.w.PoseidonHash(lenPrefData)
if err != nil {
return MerkleNode{}, err
}
var result MerkleNode var result MerkleNode
copy(result[:], b) copy(result[:], b)
@ -168,71 +210,140 @@ func (r *RLN) Hash(data []byte) (MerkleNode, error) {
// GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree. // GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree.
// The output will containt the proof data and should be parsed as |proof<128>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>| // The output will containt the proof data and should be parsed as |proof<128>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
// integers wrapped in <> indicate value sizes in bytes // integers wrapped in <> indicate value sizes in bytes
func (r *RLN) GenerateProof(data []byte, key MembershipKeyPair, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) { func (r *RLN) GenerateProof(
input := serialize(key.IDKey, index, epoch, data) data []byte,
inputBuffer := toCBufferPtr(input) key IdentityCredential,
index MembershipIndex,
epoch Epoch,
messageId uint32) (*RateLimitProof, error) {
var output []byte externalNullifierInput, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
out := toBuffer(output) if err != nil {
return nil, fmt.Errorf("could not construct the external nullifier: %w", err)
if !bool(C.generate_rln_proof(r.ptr, inputBuffer, &out)) {
return nil, errors.New("could not generate the proof")
} }
proofBytes := C.GoBytes(unsafe.Pointer(out.ptr), C.int(out.len)) input := serialize(key.IDSecretHash, index, key.UserMessageLimit, messageId, externalNullifierInput, data)
proofBytes, err := r.w.GenerateRLNProof(input)
if len(proofBytes) != 320 { if err != nil {
return nil, errors.New("invalid proof generated") return nil, err
} }
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ] 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 proofOffset := 128
rootOffset := proofOffset + 32 rootOffset := proofOffset + 32
epochOffset := rootOffset + 32 externalNullifierOffset := rootOffset + 32
shareXOffset := epochOffset + 32 shareXOffset := externalNullifierOffset + 32
shareYOffset := shareXOffset + 32 shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32 nullifierOffset := shareYOffset + 32
rlnIdentifierOffset := nullifierOffset + 32
var zkproof ZKSNARK var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode var proofRoot, shareX, shareY MerkleNode
var epochR Epoch var externalNullifier Nullifier
var nullifier Nullifier var nullifier Nullifier
var rlnIdentifier RLNIdentifier
copy(zkproof[:], proofBytes[0:proofOffset]) copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset]) copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
copy(epochR[:], proofBytes[rootOffset:epochOffset]) copy(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
copy(shareX[:], proofBytes[epochOffset:shareXOffset]) copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset])
copy(shareY[:], proofBytes[shareXOffset:shareYOffset]) copy(shareY[:], proofBytes[shareXOffset:shareYOffset])
copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset]) copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset])
copy(rlnIdentifier[:], proofBytes[nullifierOffset:rlnIdentifierOffset])
return &RateLimitProof{ return &RateLimitProof{
Proof: zkproof, Proof: zkproof,
MerkleRoot: proofRoot, MerkleRoot: proofRoot,
Epoch: epochR, ExternalNullifier: externalNullifier,
ShareX: shareX, ShareX: shareX,
ShareY: shareY, ShareY: shareY,
Nullifier: nullifier, Nullifier: nullifier,
RLNIdentifier: rlnIdentifier,
}, nil }, nil
} }
// Verify verifies a proof generated for the RLN. // Returns a RLN proof with a custom witness, so no tree is required in the RLN instance
// proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ] // to calculate such proof. The witness can be created with GetMerkleProof data.
func (r *RLN) Verify(data []byte, proof RateLimitProof) (bool, error) { func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {
proofBytes := proof.serialize(data) // serialized as: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L127
proofBuf := toCBufferPtr(proofBytes) // 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> ]
res := C.bool(false) proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
if !bool(C.verify_rln_proof(r.ptr, proofBuf, &res)) { if err != nil {
return false, errors.New("could not verify rln proof") return nil, err
} }
return bool(res), nil 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 serializeRoots(roots [][32]byte) []byte { 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
}
func serialize32(roots [][32]byte) []byte {
var result []byte
for _, r := range roots {
result = append(result, r[:]...)
}
return result
}
func serializeSlice(roots [][]byte) []byte {
var result []byte var result []byte
for _, r := range roots { for _, r := range roots {
result = append(result, r[:]...) result = append(result, r[:]...)
@ -257,65 +368,123 @@ func serializeCommitments(commitments []IDCommitment) []byte {
return result return result
} }
func (r *RLN) VerifyWithRoots(data []byte, proof RateLimitProof, roots [][32]byte) (bool, error) { func serializeIndices(indices []MembershipIndex) []byte {
proofBytes := proof.serialize(data) var result []byte
proofBuf := toCBufferPtr(proofBytes)
rootBytes := serializeRoots(roots) inputLen := make([]byte, 8)
rootBuf := toCBufferPtr(rootBytes) binary.LittleEndian.PutUint64(inputLen, uint64(len(indices)))
result = append(result, inputLen...)
res := C.bool(false) for _, index := range indices {
if !bool(C.verify_with_roots(r.ptr, proofBuf, rootBuf, &res)) { result = binary.LittleEndian.AppendUint64(result, uint64(index))
return false, errors.New("could not verify with roots")
} }
return bool(res), nil return result
} }
// InsertMember adds the member to the tree // proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
func (r *RLN) InsertMember(idComm IDCommitment) error { // validRoots should contain a sequence of roots in the acceptable windows.
idCommBuffer := toCBufferPtr(idComm[:]) // As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped
insertionSuccess := bool(C.set_next_leaf(r.ptr, idCommBuffer)) func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool, error) {
proofBytes := proof.serializeWithData(data)
rootBytes := serialize32(roots)
res, err := r.w.VerifyWithRoots(proofBytes, rootBytes)
if err != nil {
return false, err
}
return res, nil
}
// RecoverIDSecret returns an IDSecret having obtained before two proofs
func (r *RLN) RecoverIDSecret(proof1 RateLimitProof, proof2 RateLimitProof) (IDSecretHash, error) {
proof1Bytes := proof1.serialize()
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)
idCommBuffer := toCBufferPtr(idCommBytes) indicesBytes := serializeIndices(nil)
insertionSuccess := bool(C.set_leaves_from(r.ptr, C.uintptr_t(index), idCommBuffer)) 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
func (r *RLN) DeleteMember(index MembershipIndex) error { func (r *RLN) DeleteMember(index MembershipIndex) error {
deletionSuccess := bool(C.delete_leaf(r.ptr, C.uintptr_t(index))) deletionSuccess := r.w.DeleteLeaf(index)
if !deletionSuccess { if !deletionSuccess {
return errors.New("could not delete member") return errors.New("could not delete member")
} }
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) {
var output []byte b, err := r.w.GetRoot()
out := toBuffer(output) if err != nil {
return MerkleNode{}, err
if !bool(C.get_root(r.ptr, &out)) {
return MerkleNode{}, errors.New("could not get the root")
} }
b := C.GoBytes(unsafe.Pointer(out.ptr), C.int(out.len))
if len(b) != 32 { if len(b) != 32 {
return MerkleNode{}, errors.New("wrong output size") return MerkleNode{}, errors.New("wrong output size")
} }
@ -326,10 +495,48 @@ 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 []IDCommitment) error { func (r *RLN) AddAll(list []IdentityCredential) error {
for _, member := range list { for _, member := range list {
if err := r.InsertMember(member); err != nil { if err := r.InsertMember(member.IDCommitment, member.UserMessageLimit); err != nil {
return err return err
} }
} }
@ -343,11 +550,8 @@ func CalcMerkleRoot(list []IDCommitment) (MerkleNode, error) {
return MerkleNode{}, err return MerkleNode{}, err
} }
// create a Merkle tree if err := rln.InsertMembers(0, list); err != nil {
for _, c := range list { return MerkleNode{}, err
if err := rln.InsertMember(c); err != nil {
return MerkleNode{}, err
}
} }
return rln.GetMerkleRoot() return rln.GetMerkleRoot()
@ -356,14 +560,14 @@ func CalcMerkleRoot(list []IDCommitment) (MerkleNode, error) {
// CreateMembershipList produces a list of membership key pairs and also returns the root of a Merkle tree constructed // CreateMembershipList produces a list of membership key pairs and also returns the root of a Merkle tree constructed
// out of the identity commitment keys of the generated list. The output of this function is used to initialize a static // out of the identity commitment keys of the generated list. The output of this function is used to initialize a static
// group keys (to test waku-rln-relay in the off-chain mode) // group keys (to test waku-rln-relay in the off-chain mode)
func CreateMembershipList(n int) ([]MembershipKeyPair, MerkleNode, error) { func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
// initialize a Merkle tree // initialize a Merkle tree
rln, err := NewRLN() rln, err := NewRLN()
if err != nil { if err != nil {
return nil, MerkleNode{}, err return nil, MerkleNode{}, err
} }
var output []MembershipKeyPair var output []IdentityCredential
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
// generate a keypair // generate a keypair
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
@ -374,7 +578,7 @@ func CreateMembershipList(n int) ([]MembershipKeyPair, MerkleNode, error) {
output = append(output, *keypair) output = append(output, *keypair)
// insert the key to the Merkle tree // insert the key to the Merkle tree
if err := rln.InsertMember(keypair.IDCommitment); err != nil { if err := rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit); err != nil {
return nil, MerkleNode{}, err return nil, MerkleNode{}, err
} }
} }
@ -386,3 +590,42 @@ func CreateMembershipList(n int) ([]MembershipKeyPair, 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,18 +17,41 @@ 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)
key, err := rln.MembershipKeyGen() key, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
s.Len(key.IDKey, 32) s.Len(key.IDSecretHash, 32)
s.Len(key.IDCommitment, 32) s.Len(key.IDCommitment, 32)
s.NotEmpty(key.IDKey) s.Len(key.IDTrapdoor, 32)
s.Len(key.IDNullifier, 32)
s.NotEmpty(key.IDSecretHash)
s.NotEmpty(key.IDCommitment) s.NotEmpty(key.IDCommitment)
s.NotEmpty(key.IDTrapdoor)
s.NotEmpty(key.IDNullifier)
s.False(bytes.Equal(key.IDCommitment[:], make([]byte, 32))) s.False(bytes.Equal(key.IDCommitment[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDKey[:], make([]byte, 32))) s.False(bytes.Equal(key.IDSecretHash[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDTrapdoor[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDNullifier[:], make([]byte, 32)))
} }
func (s *RLNSuite) TestGetMerkleRoot() { func (s *RLNSuite) TestGetMerkleRoot() {
@ -53,25 +76,69 @@ func (s *RLNSuite) TestInsertMember() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment) err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err) s.NoError(err)
} }
func (s *RLNSuite) TestInsertRawLeaf() {
rln, err := NewRLN()
s.NoError(err)
for i := 0; i < 10; i++ {
// Generate a membership
memKeys, err := rln.MembershipKeyGen(10)
s.NoError(err)
// Calculate the leaf ourselves
userMessageLimitBytes := SerializeUint32(memKeys.UserMessageLimit)
hashedLeaf, err := rln.Poseidon(memKeys.IDCommitment[:], userMessageLimitBytes[:])
s.NoError(err)
// Insert the leaf as it is
err = rln.InsertRawLeaf(hashedLeaf)
s.NoError(err)
// Get it from the tree
retrievedLeaf, err := rln.GetLeaf(uint(i))
s.NoError(err)
// Check the retrieved matches the one we added
s.Equal(hashedLeaf, retrievedLeaf)
// Check tree size matches
numLeaves := rln.LeavesSet()
s.Equal(uint(i+1), numLeaves)
}
}
func (s *RLNSuite) TestInsertMembers() { func (s *RLNSuite) TestInsertMembers() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
keypair, err := rln.MembershipKeyGen() var commitments []IDCommitment
for i := 0; i < 10; i++ {
keypair, err := rln.MembershipKeyGen()
s.NoError(err)
commitments = append(commitments, keypair.IDCommitment)
}
err = rln.InsertMembers(0, commitments)
s.NoError(err) s.NoError(err)
err = rln.InsertMembers(0, []IDCommitment{keypair.IDCommitment}) numLeaves := rln.LeavesSet()
s.NoError(err) s.Equal(uint(10), numLeaves)
} }
func (s *RLNSuite) TestRemoveMember() { func (s *RLNSuite) TestRemoveMember() {
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)
} }
@ -87,7 +154,7 @@ func (s *RLNSuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() {
keypair, err := rln.MembershipKeyGen() keypair, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment) err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
s.NoError(err) s.NoError(err)
// read the Merkle Tree root after insertion // read the Merkle Tree root after insertion
@ -119,13 +186,27 @@ func (s *RLNSuite) TestHash() {
// prepare the input // prepare the input
msg := []byte("Hello") msg := []byte("Hello")
hash, err := rln.Hash(msg) hash, err := rln.Sha256(msg)
s.NoError(err) s.NoError(err)
expectedHash, _ := hex.DecodeString("4c6ea217404bd5f10e243bac29dc4f1ec36bf4a41caba7b4c8075c54abb3321e") expectedHash, _ := hex.DecodeString("4c6ea217404bd5f10e243bac29dc4f1ec36bf4a41caba7b4c8075c54abb3321e")
s.Equal(expectedHash, hash[:]) s.Equal(expectedHash, hash[:])
} }
func (s *RLNSuite) TestPoseidon() {
rln, err := NewRLN()
s.NoError(err)
// prepare the input
msg1, _ := hex.DecodeString("126f4c026cd731979365f79bd345a46d673c5a3f6f588bdc718e6356d02b6fdc")
msg2, _ := hex.DecodeString("1f0e5db2b69d599166ab16219a97b82b662085c93220382b39f9f911d3b943b1")
hash, err := rln.Poseidon(msg1, msg2)
s.NoError(err)
expectedHash, _ := hex.DecodeString("83e4a6b2dea68aad26f04f32f37ac1e018188a0056b158b2aa026d34266d1f30")
s.Equal(expectedHash, hash[:])
}
func (s *RLNSuite) TestCreateListMembershipKeysAndCreateMerkleTreeFromList() { func (s *RLNSuite) TestCreateListMembershipKeysAndCreateMerkleTreeFromList() {
groupSize := 100 groupSize := 100
list, root, err := CreateMembershipList(groupSize) list, root, err := CreateMembershipList(groupSize)
@ -138,7 +219,7 @@ func (s *RLNSuite) TestCheckCorrectness() {
groupKeys := STATIC_GROUP_KEYS groupKeys := STATIC_GROUP_KEYS
// create a set of MembershipKeyPair objects from groupKeys // create a set of MembershipKeyPair objects from groupKeys
groupKeyPairs, err := toMembershipKeyPairs(groupKeys) groupKeyPairs, err := ToIdentityCredentials(groupKeys)
s.NoError(err) s.NoError(err)
// extract the id commitments // extract the id commitments
@ -157,28 +238,63 @@ func (s *RLNSuite) TestCheckCorrectness() {
s.Equal(expectedRoot, root[:]) s.Equal(expectedRoot, root[:])
} }
func (s *RLNSuite) TestGetLeaf() {
rln, err := NewRLN()
s.NoError(err)
amountLeafs := int(31)
for i := 0; i < amountLeafs; i++ {
// allowed messages per epoch of the membership
// using different values between 1 and 7
userMessageLimit := uint32(amountLeafs%7 + 1)
// generate membership
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
// insert membership
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err)
// retrieve the leaf
retrievedLeaf, err := rln.GetLeaf(uint(i))
s.NoError(err)
// calculate the leaf we would expect
userMessageLimitBytes := SerializeUint32(userMessageLimit)
hashedLeaf, err := rln.Poseidon(memKeys.IDCommitment[:], userMessageLimitBytes[:])
s.NoError(err)
// assert it matches
s.Equal(hashedLeaf, retrievedLeaf)
}
}
func (s *RLNSuite) TestValidProof() { func (s *RLNSuite) TestValidProof() {
rln, err := NewRLN() rln, err := NewRLN()
s.NoError(err) s.NoError(err)
memKeys, err := rln.MembershipKeyGen() // allowed messages per epoch of the membership
s.NoError(err) userMessageLimit := uint32(10)
//peer's index in the Merkle Tree //peer's index in the Merkle Tree
index := 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 := 0; i < 10; i++ { for i := uint(0); i < 10; i++ {
if i == index { if i == index {
// insert the current peer's pk err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
err = rln.InsertMember(memKeys.IDCommitment)
s.NoError(err) s.NoError(err)
} else { } else {
// create a new key pair // create a new key pair
memberKeys, err := rln.MembershipKeyGen() memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment) err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
} }
} }
@ -187,24 +303,75 @@ func (s *RLNSuite) TestValidProof() {
msg := []byte("Hello") msg := []byte("Hello")
// prepare the epoch // prepare the epoch
var epoch Epoch var epoch Epoch = SerializeUint32(1000)
// generate proof // generate multiple valid proofs for the same epoch
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch) for i := uint32(0); i < userMessageLimit; i++ {
// message sequence within the epoch
messageId := uint32(i)
// generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
s.NoError(err)
// verify the proof
verified, err := rln.Verify(msg, *proofRes)
s.NoError(err)
s.True(verified)
// verify with roots
root, err := rln.GetMerkleRoot()
s.NoError(err)
verified, err = rln.Verify(msg, *proofRes, root)
s.NoError(err)
s.True(verified)
}
}
func (s *RLNSuite) TestProofBeyondLimit() {
rln, err := NewRLN()
s.NoError(err) s.NoError(err)
// verify the proof // allowed messages per epoch of the membership
verified, err := rln.Verify(msg, *proofRes) userMessageLimit := uint32(10)
s.NoError(err)
s.True(verified)
// verify with roots //peer's index in the Merkle Tree
root, err := rln.GetMerkleRoot() index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err) s.NoError(err)
verified, err = rln.VerifyWithRoots(msg, *proofRes, [][32]byte{root}) // Create a Merkle tree with random members
s.NoError(err) for i := uint(0); i < 10; i++ {
s.True(verified) if i == index {
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
}
}
// prepare the message
msg := []byte("Hello")
// prepare the epoch
var epoch Epoch = SerializeUint32(876543456)
// TODO;:
for i := uint32(userMessageLimit + 1); i < (userMessageLimit + 10); i++ {
// message sequence within the epoch
messageId := uint32(i)
// generate proof TODO:Errors!
_, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
s.Error(err)
}
} }
func (s *RLNSuite) TestInvalidProof() { func (s *RLNSuite) TestInvalidProof() {
@ -221,18 +388,21 @@ func (s *RLNSuite) TestInvalidProof() {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if i == index { if i == index {
// insert the current peer's pk // insert the current peer's pk
err := rln.InsertMember(memKeys.IDCommitment) err := rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
} else { } else {
// create a new key pair // create a new key pair
memberKeys, err := rln.MembershipKeyGen() memberKeys, err := rln.MembershipKeyGen()
s.NoError(err) s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment) err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err) s.NoError(err)
} }
} }
root, err := rln.GetMerkleRoot()
s.NoError(err)
// prepare the message // prepare the message
msg := []byte("Hello") msg := []byte("Hello")
@ -241,16 +411,245 @@ func (s *RLNSuite) TestInvalidProof() {
badIndex := 4 badIndex := 4
// message sequence within the epoch
messageId := uint32(1)
// generate proof // generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch) proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch, messageId)
s.NoError(err) s.NoError(err)
// verify the proof (should not be verified) // verify the proof (should not be verified)
verified, err := rln.Verify(msg, *proofRes) verified, err := rln.Verify(msg, *proofRes, root)
s.NoError(err) s.NoError(err)
s.False(verified) s.False(verified)
} }
func (s *RLNSuite) TestGetMerkleProof() {
for _, treeDepth := range []TreeDepth{TreeDepth15, TreeDepth19, TreeDepth20} {
treeDepthInt := int(treeDepth)
rln, err := NewWithConfig(treeDepth, nil)
s.NoError(err)
leaf0 := [32]byte{0x00}
leaf1 := [32]byte{0x01}
leaf5 := [32]byte{0x05}
rln.InsertMemberAt(0, leaf0)
rln.InsertMemberAt(1, leaf1)
rln.InsertMemberAt(5, leaf5)
b1, err := rln.GetMerkleProof(0)
s.NoError(err)
s.Equal(treeDepthInt, len(b1.PathElements))
s.Equal(treeDepthInt, len(b1.PathIndexes))
// First path is right leaf [0, 1]
s.EqualValues(leaf1, b1.PathElements[0])
b2, err := rln.GetMerkleProof(4)
s.NoError(err)
s.Equal(treeDepthInt, len(b2.PathElements))
s.Equal(treeDepthInt, len(b2.PathIndexes))
// First path is right leaf [4, 5]
s.EqualValues(leaf5, b2.PathElements[0])
b3, err := rln.GetMerkleProof(10)
s.NoError(err)
s.Equal(treeDepthInt, len(b3.PathElements))
s.Equal(treeDepthInt, len(b3.PathIndexes))
// First path is right leaf. But its empty
s.EqualValues([32]byte{0x00}, b3.PathElements[0])
}
}
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
treeSize := 20
userMessageLimit := uint32(100)
message := []byte("some rln protected message")
rln, err := NewRLN()
s.NoError(err)
treeElements := make([]IdentityCredential, 0)
// Create a Merkle tree with random members
for i := 0; i < treeSize; i++ {
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
treeElements = append(treeElements, *memberKeys)
}
// For different leafs (with a custom witness)
for _, memberIndex := range []uint{0, 10, 13} {
root, err := rln.GetMerkleRoot()
s.NoError(err)
// We provide out custom witness
merkleProof, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
// For different epochs
for _, epoch := range []Epoch{ToEpoch(1), ToEpoch(9998765)} {
// For some possible message ids
for _, messageId := range []uint32{0, 50, 99} {
rlnWitness, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
// Generate a proof with our custom witness (Merkle Path of the memberIndex)
proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness)
s.NoError(err)
verified1, err := rln.Verify(message, *proofRes1, root)
s.NoError(err)
s.True(verified1)
// Generate a proof without our custom witness, to ensure they match
proofRes2, err := rln.GenerateProof(message, treeElements[memberIndex], MembershipIndex(memberIndex), epoch, messageId)
s.NoError(err)
// Ensure we have the same root
s.Equal(root, proofRes1.MerkleRoot)
// Proof generate with custom witness match the proof generate with the witness
// from zerokit. Proof itself is not asserted, can be different.
s.Equal(proofRes1.MerkleRoot, proofRes2.MerkleRoot)
s.Equal(proofRes1.ExternalNullifier, proofRes2.ExternalNullifier)
s.Equal(proofRes1.ShareX, proofRes2.ShareX)
s.Equal(proofRes1.ShareY, proofRes2.ShareY)
s.Equal(proofRes1.Nullifier, proofRes2.Nullifier)
}
}
}
}
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() {
treeSize := 20
rln, err := NewRLN()
s.NoError(err)
treeElements := make([]IdentityCredential, 0)
// Create a Merkle tree with random members
for i := 0; i < treeSize; i++ {
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
s.NoError(err)
treeElements = append(treeElements, *memberKeys)
}
// We generate proofs with a custom witness aquired outside zerokit for diferent indexes
for _, memberIndex := range []uint{0, 10, 13, 15} {
root, err := rln.GetMerkleRoot()
s.NoError(err)
// We provide out custom witness
merkleProof, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
message := []byte("some rln protected message")
epoch := ToEpoch(1000)
userMessageLimit := uint32(10)
messageId := uint32(1)
rlnWitness1, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
// Generate a proof with our custom witness (Merkle Path of the memberIndex)
proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness1)
s.NoError(err)
// 1) Message changed, does not verify
verified1, err := rln.Verify([]byte("different message"), *proofRes1, root)
s.NoError(err)
s.False(verified1)
// 2) Different nullifier (epoch or rln id), does not verify
proofRes1.ExternalNullifier = [32]byte{0x11}
verified2, err := rln.Verify(message, *proofRes1, root)
s.NoError(err)
s.False(verified2)
// 3) Merkle proof in provided witness is wrong, does not verify
merkleProof.PathElements[0] = [32]byte{0x11}
rlnWitness2, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
messageId,
message,
epoch,
merkleProof)
s.NoError(err)
proofRes3, err := rln.GenerateRLNProofWithWitness(rlnWitness2)
s.NoError(err)
verified3, err := rln.Verify(message, *proofRes3, root)
s.NoError(err)
s.False(verified3)
// 4) Membership does not match the index (and not part of tree), does not verify
merkleProof4, err := rln.GetMerkleProof(memberIndex)
s.NoError(err)
// Membership that does not match the index
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
// Proof proves memberIndex inclusion, but provided membership is different
rlnWitness4, err := rln.CreateWitness(
memberKeys.IDSecretHash,
userMessageLimit,
messageId,
[]byte("some rln protected message"),
ToEpoch(999),
merkleProof4)
s.NoError(err)
proofRes4, err := rln.GenerateRLNProofWithWitness(rlnWitness4)
s.NoError(err)
verified4, err := rln.Verify(message, *proofRes4, root)
s.NoError(err)
s.False(verified4)
// 5) Message id goes beyond the userMessageLimit, does not generate
wrongMessageId := uint32(1000)
rlnWitness5, err := rln.CreateWitness(
treeElements[memberIndex].IDSecretHash,
userMessageLimit,
wrongMessageId,
message,
epoch,
merkleProof)
s.NoError(err)
_, err = rln.GenerateRLNProofWithWitness(rlnWitness5)
s.Error(err)
}
}
func (s *RLNSuite) TestEpochConsistency() { func (s *RLNSuite) TestEpochConsistency() {
// check edge cases // check edge cases
var epoch uint64 = math.MaxUint64 var epoch uint64 = math.MaxUint64

View File

@ -1,20 +1,37 @@
package rln package rln
import "encoding/binary" import (
"encoding/binary"
"errors"
"fmt"
"math/big"
)
// serialize converts a RateLimitProof and the data to a byte seq // serialize converts a RateLimitProof and the data to a byte seq
// this conversion is used in the proofGen function // format taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L747
// the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146 // [identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ]
// [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ] func serialize(
func serialize(idKey IDKey, memIndex MembershipIndex, epoch Epoch, msg []byte) []byte { idKey IDSecretHash,
memIndex MembershipIndex,
userMessageLimit uint32,
messageId uint32,
externalNullifier [32]byte,
msg []byte) []byte {
memIndexBytes := make([]byte, 8) memIndexBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex)) binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex))
lenPrefMsg := appendLength(msg) lenPrefMsg := appendLength(msg)
var userMessageLimitByte [32]byte
var messageIdByte [32]byte
binary.LittleEndian.PutUint32(userMessageLimitByte[0:], userMessageLimit)
binary.LittleEndian.PutUint32(messageIdByte[0:], messageId)
output := append(idKey[:], memIndexBytes...) output := append(idKey[:], memIndexBytes...)
output = append(output, epoch[:]...) output = append(output, userMessageLimitByte[:]...)
output = append(output, messageIdByte[:]...)
output = append(output, externalNullifier[:]...)
output = append(output, lenPrefMsg...) output = append(output, lenPrefMsg...)
return output return output
@ -23,16 +40,111 @@ func serialize(idKey IDKey, memIndex MembershipIndex, epoch Epoch, msg []byte) [
// serialize converts a RateLimitProof and data to a byte seq // serialize converts a RateLimitProof and data to a byte seq
// this conversion is used in the proof verification proc // this conversion is used in the proof verification proc
// the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205 // the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
// [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ] // [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal<var> ]
func (r RateLimitProof) serialize(data []byte) []byte { func (r RateLimitProof) serializeWithData(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.Epoch[:]...) proofBytes = append(proofBytes, r.ExternalNullifier[:]...)
proofBytes = append(proofBytes, r.ShareX[:]...) proofBytes = append(proofBytes, r.ShareX[:]...)
proofBytes = append(proofBytes, r.ShareY[:]...) proofBytes = append(proofBytes, r.ShareY[:]...)
proofBytes = append(proofBytes, r.Nullifier[:]...) proofBytes = append(proofBytes, r.Nullifier[:]...)
proofBytes = append(proofBytes, r.RLNIdentifier[:]...)
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
}

65
rln/serialize_test.go Normal file
View File

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

File diff suppressed because one or more lines are too long

View File

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

53
rln/utils_test.go Normal file
View File

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

View File

@ -1,32 +0,0 @@
[target.x86_64-pc-windows-gnu]
image = "ghcr.io/cross-rs/x86_64-pc-windows-gnu:local"
[target.aarch64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:local"
[target.x86_64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:local"
[target.arm-unknown-linux-gnueabi]
image = "ghcr.io/cross-rs/arm-unknown-linux-gnueabi:local"
[target.i686-pc-windows-gnu]
image = "ghcr.io/cross-rs/i686-pc-windows-gnu:local"
[target.i686-unknown-linux-gnu]
image = "ghcr.io/cross-rs/i686-unknown-linux-gnu:local"
[target.arm-unknown-linux-gnueabihf]
image = "ghcr.io/cross-rs/arm-unknown-linux-gnueabihf:local"
[target.mips-unknown-linux-gnu]
image = "ghcr.io/cross-rs/mips-unknown-linux-gnu:local"
[target.mips64-unknown-linux-gnuabi64]
image = "ghcr.io/cross-rs/mips64-unknown-linux-gnuabi64:local"
[target.mips64el-unknown-linux-gnuabi64]
image = "ghcr.io/cross-rs/mips64el-unknown-linux-gnuabi64:local"
[target.mipsel-unknown-linux-gnu]
image = "ghcr.io/cross-rs/mipsel-unknown-linux-gnu:local"

View File

@ -1,53 +0,0 @@
#!/bin/bash
DIRECTORY=./libs
if [[ -d "$DIRECTORY" ]]
then
echo "$DIRECTORY exists on your filesystem. Delete it and run the script again."
exit 0
fi
export RUSTFLAGS="-Ccodegen-units=1"
rustup default stable
cargo install cross --git https://github.com/cross-rs/cross --branch main
pushd zerokit/rln
cargo clean
cross build --release --lib --target=aarch64-unknown-linux-gnu
cross build --release --lib --target=arm-unknown-linux-gnueabi
cross build --release --lib --target=arm-unknown-linux-gnueabihf
cross build --release --lib --target=i686-pc-windows-gnu
cross build --release --lib --target=i686-unknown-linux-gnu
cross build --release --lib --target=x86_64-pc-windows-gnu
cross build --release --lib --target=x86_64-unknown-linux-gnu
cross build --release --lib --target=x86_64-unknown-linux-musl
#cross build --release --lib --target=aarch64-linux-android
#cross build --release --lib --target=armv7-linux-androideabi
#cross build --release --lib --target=i686-linux-android
#cross build --release --lib --target=x86_64-linux-android
# TODO: these work only on iOS
cargo install cargo-lipo
rustup target add aarch64-apple-ios x86_64-apple-ios x86_64-apple-darwin aarch64-apple-darwin
cargo build --release --target=x86_64-apple-darwin --lib
cargo build --release --target=aarch64-apple-darwin --lib
#cargo build --release --target=x86_64-apple-ios --lib
#cargo build --release --target=aarch64-apple-ios --lib
cargo lipo --release
popd
TOOLS_DIR=`dirname $0`
COMPILE_DIR=${TOOLS_DIR}/../zerokit/target
rm -rf $COMPILE_DIR/x86_64-apple-ios $COMPILE_DIR/aarch64-apple-ios
for platform in `ls ${COMPILE_DIR} | grep -v release | grep -v debug`
do
PLATFORM_DIR=${DIRECTORY}/$platform
mkdir -p ${PLATFORM_DIR}
cp ${COMPILE_DIR}/$platform/release/librln.a ${PLATFORM_DIR}
done

@ -1 +0,0 @@
Subproject commit 284e51483c73e956c2c318fe6f69d735412bdc08