Compare commits

..

2 Commits

Author SHA1 Message Date
Richard Ramos
6883d9bb26
chore: update zerokit 2022-10-27 11:09:35 -04:00
Richard Ramos
27e095006c
chore: change project org 2022-10-27 09:10:01 -04:00
36 changed files with 1020 additions and 1922 deletions

View File

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

4
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

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

12
Makefile Normal file
View File

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

18
go.mod
View File

@ -1,23 +1,13 @@
module github.com/waku-org/go-zerokit-rln
go 1.19
go 1.17
require github.com/stretchr/testify v1.7.2
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/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

47
go.sum
View File

@ -1,53 +1,20 @@
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/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/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/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.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
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=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

66
rln/librln.h Normal file
View File

@ -0,0 +1,66 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define TEST_PARAMETERS_INDEX 2
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(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);

16
rln/link.go Normal file
View File

@ -0,0 +1,16 @@
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"

View File

@ -1,130 +0,0 @@
//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()
}

View File

@ -1,129 +0,0 @@
//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()
}

View File

@ -1,130 +0,0 @@
//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()
}

345
rln/resources/bindata.go Normal file

File diff suppressed because one or more lines are too long

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

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

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,119 @@
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 6,
"vk_alpha_1": [
"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,163 +1,121 @@
package rln
/*
#include "./librln.h"
*/
import "C"
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"unsafe"
"github.com/waku-org/go-zerokit-rln/rln/link"
"github.com/waku-org/go-zerokit-rln/rln/resources"
)
// Same as: https://github.com/vacp2p/zerokit/blob/v0.3.5/rln/src/public.rs#L35
// Prevents a RLN ZK proof generated for one application to be re-used in another one.
var RLN_IDENTIFIER = [32]byte{166, 140, 43, 8, 8, 22, 206, 113, 151, 128, 118, 40, 119, 197, 218, 174, 11, 117, 84, 228, 96, 211, 212, 140, 145, 104, 146, 99, 24, 192, 217, 4}
var DEFAULT_USER_MESSAGE_LIMIT = uint32(10)
// RLN represents the context used for rln.
type RLN struct {
w *link.RLNWrapper
}
func getResourcesFolder(depth TreeDepth) string {
return fmt.Sprintf("tree_height_%d", depth)
ptr *C.RLN
}
// 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
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
// and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree
func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte, treeConfig *TreeConfig) (*RLN, error) {
func NewRLNWithParams(depth int, wasm []byte, zkey []byte, verifKey []byte) (*RLN, error) {
r := &RLN{}
var err error
treeConfigBytes := []byte{}
if treeConfig != nil {
treeConfigBytes, err = json.Marshal(treeConfig)
if err != nil {
return nil, err
}
}
wasmBuffer := toCBufferPtr(wasm)
zkeyBuffer := toCBufferPtr(zkey)
verifKeyBuffer := toCBufferPtr(verifKey)
r.w, err = link.NewWithParams(depth, wasm, zkey, verifKey, treeConfigBytes)
if err != nil {
return nil, err
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
}
// NewWithConfig generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `depth` indicates the depth of Merkle tree
func NewWithConfig(depth TreeDepth, treeConfig *TreeConfig) (*RLN, error) {
// NewRLNWithFolder generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `deptk` indicates the depth of Merkle tree
// The parameter “
func NewRLNWithFolder(depth int, resourcesFolderPath string) (*RLN, error) {
r := &RLN{}
var err error
configBytes, err := json.Marshal(config{
ResourcesFolder: getResourcesFolder(depth),
TreeConfig: treeConfig,
})
if err != nil {
return nil, err
}
pathBuffer := toCBufferPtr([]byte(resourcesFolderPath))
r.w, err = link.New(int(depth), configBytes)
if err != nil {
return nil, err
if !bool(C.new(C.uintptr_t(depth), pathBuffer, &r.ptr)) {
return nil, errors.New("failed to initialize")
}
return r, nil
}
func (r *RLN) SetTree(treeHeight uint) error {
success := r.w.SetTree(treeHeight)
if !success {
return errors.New("could not set tree height")
}
return nil
func toCBufferPtr(input []byte) *C.Buffer {
buf := toBuffer(input)
size := int(unsafe.Sizeof(buf))
in := (*C.Buffer)(C.malloc(C.size_t(size)))
*in = buf
return in
}
// Initialize merkle tree with a list of IDCommitments
func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
idCommBytes := serializeCommitments(idComms)
initSuccess := r.w.InitTreeWithLeaves(idCommBytes)
if !initSuccess {
return errors.New("could not init tree")
}
return nil
}
func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*IdentityCredential, error) {
// add user message limit
key := &IdentityCredential{
IDTrapdoor: [32]byte{},
IDNullifier: [32]byte{},
IDSecretHash: [32]byte{},
IDCommitment: [32]byte{},
UserMessageLimit: userMessageLimit,
// MembershipKeyGen generates a MembershipKeyPair that can be used for the registration into the rln membership contract
func (r *RLN) MembershipKeyGen() (*MembershipKeyPair, error) {
buffer := toBuffer([]byte{})
if !bool(C.key_gen(r.ptr, &buffer)) {
return nil, errors.New("error in key generation")
}
if len(generatedKeys) != 32*4 {
return nil, errors.New("generated keys are of invalid length")
key := &MembershipKeyPair{
IDKey: [32]byte{},
IDCommitment: [32]byte{},
}
copy(key.IDTrapdoor[:], generatedKeys[:32])
copy(key.IDNullifier[:], generatedKeys[32:64])
copy(key.IDSecretHash[:], generatedKeys[64:96])
copy(key.IDCommitment[:], generatedKeys[96:128])
// the public and secret keys together are 64 bytes
generatedKeys := C.GoBytes(unsafe.Pointer(buffer.ptr), C.int(buffer.len))
if len(generatedKeys) != 64 {
return nil, errors.New("the generated keys are invalid")
}
copy(key.IDKey[:], generatedKeys[:32])
copy(key.IDCommitment[:], generatedKeys[32:64])
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
// [len<8>|input<var>], the len is a 8 byte value serialized in little endian
func appendLength(input []byte) []byte {
@ -166,40 +124,40 @@ func appendLength(input []byte) []byte {
return append(inputLen, input...)
}
// Similar to appendLength but for 32 byte values. The length that is prepended is
// the length of elements that are 32 bytes long each
func appendLength32(input []byte) []byte {
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(input)/32))
return append(inputLen, input...)
// toBuffer converts the input to a buffer object that is used to communicate data with the rln lib
func toBuffer(data []byte) C.Buffer {
dataPtr, dataLen := sliceToPtr(data)
return C.Buffer{
ptr: dataPtr,
len: C.uintptr_t(dataLen),
}
}
func (r *RLN) Sha256(data []byte) (MerkleNode, error) {
func sliceToPtr(slice []byte) (*C.uchar, C.int) {
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)
b, err := r.w.Hash(lenPrefData)
if err != nil {
return MerkleNode{}, err
hashInputBuffer := toCBufferPtr(lenPrefData)
var output []byte
out := toBuffer(output)
if !bool(C.hash(r.ptr, hashInputBuffer, &out)) {
return MerkleNode{}, errors.New("failed to hash")
}
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
}
b := C.GoBytes(unsafe.Pointer(out.ptr), C.int(out.len))
var result MerkleNode
copy(result[:], b)
@ -210,246 +168,97 @@ func (r *RLN) Poseidon(input ...[]byte) (MerkleNode, error) {
// 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>|
// integers wrapped in <> indicate value sizes in bytes
func (r *RLN) GenerateProof(
data []byte,
key IdentityCredential,
index MembershipIndex,
epoch Epoch,
messageId uint32) (*RateLimitProof, error) {
func (r *RLN) GenerateProof(data []byte, key MembershipKeyPair, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) {
input := serialize(key.IDKey, index, epoch, data)
inputBuffer := toCBufferPtr(input)
externalNullifierInput, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
if err != nil {
return nil, fmt.Errorf("could not construct the external nullifier: %w", err)
var output []byte
out := toBuffer(output)
if !bool(C.generate_rln_proof(r.ptr, inputBuffer, &out)) {
return nil, errors.New("could not generate the proof")
}
input := serialize(key.IDSecretHash, index, key.UserMessageLimit, messageId, externalNullifierInput, data)
proofBytes, err := r.w.GenerateRLNProof(input)
if err != nil {
return nil, err
proofBytes := C.GoBytes(unsafe.Pointer(out.ptr), C.int(out.len))
if len(proofBytes) != 320 {
return nil, errors.New("invalid proof generated")
}
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>]
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
proofOffset := 128
rootOffset := proofOffset + 32
externalNullifierOffset := rootOffset + 32
shareXOffset := externalNullifierOffset + 32
epochOffset := rootOffset + 32
shareXOffset := epochOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
rlnIdentifierOffset := nullifierOffset + 32
var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode
var externalNullifier Nullifier
var epochR Epoch
var nullifier Nullifier
var rlnIdentifier RLNIdentifier
copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
copy(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset])
copy(epochR[:], proofBytes[rootOffset:epochOffset])
copy(shareX[:], proofBytes[epochOffset:shareXOffset])
copy(shareY[:], proofBytes[shareXOffset:shareYOffset])
copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset])
copy(rlnIdentifier[:], proofBytes[nullifierOffset:rlnIdentifierOffset])
return &RateLimitProof{
Proof: zkproof,
MerkleRoot: proofRoot,
ExternalNullifier: externalNullifier,
ShareX: shareX,
ShareY: shareY,
Nullifier: nullifier,
Proof: zkproof,
MerkleRoot: proofRoot,
Epoch: epochR,
ShareX: shareX,
ShareY: shareY,
Nullifier: nullifier,
RLNIdentifier: rlnIdentifier,
}, nil
}
// Returns a RLN proof with a custom witness, so no tree is required in the RLN instance
// to calculate such proof. The witness can be created with GetMerkleProof data.
func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {
// serialized as: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L127
// input [ id_secret_hash<32> | user_message_limit<32> | message_id<32> | num_elements<8> | path_elements<var1> | num_indexes<8> | path_indexes<var2> | external_nullifier<32> ]
proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
if err != nil {
return nil, err
}
expectedBytes := 288
if len(proofBytes) != expectedBytes {
return nil, fmt.Errorf("invalid proof generated. size: %d expected: %d",
len(proofBytes), expectedBytes)
}
// parse proof taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L750
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
proofOffset := 128
rootOffset := proofOffset + 32
externalNullifierOffset := rootOffset + 32
shareXOffset := externalNullifierOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode
var externalNullifier Nullifier
var nullifier Nullifier
copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
copy(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
copy(shareX[:], proofBytes[externalNullifierOffset:shareXOffset])
copy(shareY[:], proofBytes[shareXOffset:shareYOffset])
copy(nullifier[:], proofBytes[shareYOffset:nullifierOffset])
return &RateLimitProof{
Proof: zkproof,
MerkleRoot: proofRoot,
ExternalNullifier: externalNullifier,
ShareX: shareX,
ShareY: shareY,
Nullifier: nullifier,
}, nil
}
func (r *RLN) CreateWitness(
idSecretHash IDSecretHash,
userMessageLimit uint32,
messageId uint32,
data []byte,
epoch [32]byte,
merkleProof MerkleProof) (RLNWitnessInput, error) {
externalNullifier, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
if err != nil {
return RLNWitnessInput{}, fmt.Errorf("could not construct the external nullifier: %w", err)
}
return RLNWitnessInput{
IDSecretHash: idSecretHash,
UserMessageLimit: userMessageLimit,
MessageId: messageId,
MerkleProof: merkleProof,
X: HashToBN255(data),
ExternalNullifier: externalNullifier,
}, nil
}
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
for _, r := range roots {
result = append(result, r[:]...)
}
return result
}
func serializeCommitments(commitments []IDCommitment) []byte {
// serializes a seq of IDCommitments to a byte seq
// the serialization is based on https://github.com/status-im/nwaku/blob/37bd29fbc37ce5cf636734e7dd410b1ed27b88c8/waku/v2/protocol/waku_rln_relay/rln.nim#L142
// the order of serialization is |id_commitment_len<8>|id_commitment<var>|
var result []byte
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(commitments)))
result = append(result, inputLen...)
for _, idComm := range commitments {
result = append(result, idComm[:]...)
}
return result
}
func serializeIndices(indices []MembershipIndex) []byte {
var result []byte
inputLen := make([]byte, 8)
binary.LittleEndian.PutUint64(inputLen, uint64(len(indices)))
result = append(result, inputLen...)
for _, index := range indices {
result = binary.LittleEndian.AppendUint64(result, uint64(index))
}
return result
}
// Verify verifies a proof generated for the RLN.
// proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
// validRoots should contain a sequence of roots in the acceptable windows.
// As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped
func (r *RLN) Verify(data []byte, proof RateLimitProof, roots ...[32]byte) (bool, error) {
proofBytes := proof.serializeWithData(data)
rootBytes := serialize32(roots)
res, err := r.w.VerifyWithRoots(proofBytes, rootBytes)
if err != nil {
return false, err
func (r *RLN) Verify(data []byte, proof RateLimitProof) (bool, error) {
proofBytes := proof.serialize(data)
proofBuf := toCBufferPtr(proofBytes)
res := C.bool(false)
if !bool(C.verify_rln_proof(r.ptr, proofBuf, &res)) {
return false, errors.New("could not verify rln proof")
}
return res, nil
return bool(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
func serializeRoots(roots [][32]byte) []byte {
var result []byte
for _, r := range roots {
result = append(result, r[:]...)
}
var result IDSecretHash
copy(result[:], secret)
return result, nil
return result
}
// 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)
func (r *RLN) VerifyWithRoots(data []byte, proof RateLimitProof, roots [][32]byte) (bool, error) {
proofBytes := proof.serialize(data)
proofBuf := toCBufferPtr(proofBytes)
hashedLeaf, err := r.Poseidon(idComm[:], userMessageLimitBytes[:])
if err != nil {
return err
rootBytes := serializeRoots(roots)
rootBuf := toCBufferPtr(rootBytes)
res := C.bool(false)
if !bool(C.verify_with_roots(r.ptr, proofBuf, rootBuf, &res)) {
return false, errors.New("could not verify with roots")
}
insertionSuccess := r.w.SetNextLeaf(hashedLeaf[:])
if !insertionSuccess {
return errors.New("could not insert member")
}
return nil
return bool(res), 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
// 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 {
idCommBytes := serializeCommitments(idComms)
indicesBytes := serializeIndices(nil)
insertionSuccess := r.w.AtomicOperation(index, idCommBytes, indicesBytes)
if !insertionSuccess {
return errors.New("could not insert members")
}
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[:])
// InsertMember adds the member to the tree
func (r *RLN) InsertMember(idComm IDCommitment) error {
idCommBuffer := toCBufferPtr(idComm[:])
insertionSuccess := bool(C.set_next_leaf(r.ptr, idCommBuffer))
if !insertionSuccess {
return errors.New("could not insert member")
}
@ -460,31 +269,24 @@ func (r *RLN) InsertMemberAt(index MembershipIndex, idComm IDCommitment) error {
// 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
func (r *RLN) DeleteMember(index MembershipIndex) error {
deletionSuccess := r.w.DeleteLeaf(index)
deletionSuccess := bool(C.delete_leaf(r.ptr, C.uintptr_t(index)))
if !deletionSuccess {
return errors.New("could not delete member")
}
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
func (r *RLN) GetMerkleRoot() (MerkleNode, error) {
b, err := r.w.GetRoot()
if err != nil {
return MerkleNode{}, err
var output []byte
out := toBuffer(output)
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 {
return MerkleNode{}, errors.New("wrong output size")
}
@ -495,48 +297,10 @@ func (r *RLN) GetMerkleRoot() (MerkleNode, error) {
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
func (r *RLN) AddAll(list []IdentityCredential) error {
func (r *RLN) AddAll(list []IDCommitment) error {
for _, member := range list {
if err := r.InsertMember(member.IDCommitment, member.UserMessageLimit); err != nil {
if err := r.InsertMember(member); err != nil {
return err
}
}
@ -550,8 +314,11 @@ func CalcMerkleRoot(list []IDCommitment) (MerkleNode, error) {
return MerkleNode{}, err
}
if err := rln.InsertMembers(0, list); err != nil {
return MerkleNode{}, err
// create a Merkle tree
for _, c := range list {
if err := rln.InsertMember(c); err != nil {
return MerkleNode{}, err
}
}
return rln.GetMerkleRoot()
@ -560,14 +327,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
// 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)
func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
func CreateMembershipList(n int) ([]MembershipKeyPair, MerkleNode, error) {
// initialize a Merkle tree
rln, err := NewRLN()
if err != nil {
return nil, MerkleNode{}, err
}
var output []IdentityCredential
var output []MembershipKeyPair
for i := 0; i < n; i++ {
// generate a keypair
keypair, err := rln.MembershipKeyGen()
@ -578,7 +345,7 @@ func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
output = append(output, *keypair)
// insert the key to the Merkle tree
if err := rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit); err != nil {
if err := rln.InsertMember(keypair.IDCommitment); err != nil {
return nil, MerkleNode{}, err
}
}
@ -590,42 +357,3 @@ func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
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,41 +17,18 @@ type RLNSuite struct {
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() {
rln, err := NewRLN()
s.NoError(err)
key, err := rln.MembershipKeyGen()
s.NoError(err)
s.Len(key.IDSecretHash, 32)
s.Len(key.IDKey, 32)
s.Len(key.IDCommitment, 32)
s.Len(key.IDTrapdoor, 32)
s.Len(key.IDNullifier, 32)
s.NotEmpty(key.IDSecretHash)
s.NotEmpty(key.IDKey)
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.IDSecretHash[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDTrapdoor[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDNullifier[:], make([]byte, 32)))
s.False(bytes.Equal(key.IDKey[:], make([]byte, 32)))
}
func (s *RLNSuite) TestGetMerkleRoot() {
@ -76,69 +53,14 @@ func (s *RLNSuite) TestInsertMember() {
keypair, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
err = rln.InsertMember(keypair.IDCommitment)
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() {
rln, err := NewRLN()
s.NoError(err)
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)
numLeaves := rln.LeavesSet()
s.Equal(uint(10), numLeaves)
}
func (s *RLNSuite) TestRemoveMember() {
rln, err := NewRLN()
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))
s.NoError(err)
}
@ -154,7 +76,7 @@ func (s *RLNSuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() {
keypair, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
err = rln.InsertMember(keypair.IDCommitment)
s.NoError(err)
// read the Merkle Tree root after insertion
@ -186,27 +108,13 @@ func (s *RLNSuite) TestHash() {
// prepare the input
msg := []byte("Hello")
hash, err := rln.Sha256(msg)
hash, err := rln.Hash(msg)
s.NoError(err)
expectedHash, _ := hex.DecodeString("4c6ea217404bd5f10e243bac29dc4f1ec36bf4a41caba7b4c8075c54abb3321e")
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() {
groupSize := 100
list, root, err := CreateMembershipList(groupSize)
@ -219,7 +127,7 @@ func (s *RLNSuite) TestCheckCorrectness() {
groupKeys := STATIC_GROUP_KEYS
// create a set of MembershipKeyPair objects from groupKeys
groupKeyPairs, err := ToIdentityCredentials(groupKeys)
groupKeyPairs, err := toMembershipKeyPairs(groupKeys)
s.NoError(err)
// extract the id commitments
@ -238,63 +146,28 @@ func (s *RLNSuite) TestCheckCorrectness() {
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() {
rln, err := NewRLN()
s.NoError(err)
// allowed messages per epoch of the membership
userMessageLimit := uint32(10)
//peer's index in the Merkle Tree
index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
memKeys, err := rln.MembershipKeyGen()
s.NoError(err)
//peer's index in the Merkle Tree
index := 5
// Create a Merkle tree with random members
for i := uint(0); i < 10; i++ {
for i := 0; i < 10; i++ {
if i == index {
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
// insert the current peer's pk
err = rln.InsertMember(memKeys.IDCommitment)
s.NoError(err)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
err = rln.InsertMember(memberKeys.IDCommitment)
s.NoError(err)
}
}
@ -303,75 +176,24 @@ func (s *RLNSuite) TestValidProof() {
msg := []byte("Hello")
// prepare the epoch
var epoch Epoch = SerializeUint32(1000)
var epoch Epoch
// generate multiple valid proofs for the same 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()
// generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch)
s.NoError(err)
// allowed messages per epoch of the membership
userMessageLimit := uint32(10)
// verify the proof
verified, err := rln.Verify(msg, *proofRes)
s.NoError(err)
s.True(verified)
//peer's index in the Merkle Tree
index := uint(5)
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
// verify with roots
root, err := rln.GetMerkleRoot()
s.NoError(err)
// Create a Merkle tree with random members
for i := uint(0); i < 10; i++ {
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)
}
verified, err = rln.VerifyWithRoots(msg, *proofRes, [][32]byte{root})
s.NoError(err)
s.True(verified)
}
func (s *RLNSuite) TestInvalidProof() {
@ -388,21 +210,18 @@ func (s *RLNSuite) TestInvalidProof() {
for i := 0; i < 10; i++ {
if i == index {
// insert the current peer's pk
err := rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
err := rln.InsertMember(memKeys.IDCommitment)
s.NoError(err)
} else {
// create a new key pair
memberKeys, err := rln.MembershipKeyGen()
s.NoError(err)
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
err = rln.InsertMember(memberKeys.IDCommitment)
s.NoError(err)
}
}
root, err := rln.GetMerkleRoot()
s.NoError(err)
// prepare the message
msg := []byte("Hello")
@ -411,245 +230,16 @@ func (s *RLNSuite) TestInvalidProof() {
badIndex := 4
// message sequence within the epoch
messageId := uint32(1)
// generate proof
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch, messageId)
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch)
s.NoError(err)
// verify the proof (should not be verified)
verified, err := rln.Verify(msg, *proofRes, root)
verified, err := rln.Verify(msg, *proofRes)
s.NoError(err)
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() {
// check edge cases
var epoch uint64 = math.MaxUint64

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -1,53 +1,26 @@
package rln
import (
"encoding/binary"
"encoding/hex"
"hash"
"math/big"
"sync"
import "encoding/hex"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"golang.org/x/crypto/sha3"
)
func ToIdentityCredentials(groupKeys [][]string) ([]IdentityCredential, error) {
func toMembershipKeyPairs(groupKeys [][]string) ([]MembershipKeyPair, error) {
// groupKeys is sequence of membership key tuples in the form of (identity key, identity commitment) all in the hexadecimal format
// the toIdentityCredentials proc populates a sequence of IdentityCredentials using the supplied groupKeys
// Returns an error if the conversion fails
// the toMembershipKeyPairs proc populates a sequence of MembershipKeyPairs using the supplied groupKeys
var groupIdCredentials []IdentityCredential
for _, gk := range groupKeys {
idTrapdoor, err := ToBytes32LE(gk[0])
groupKeyPairs := []MembershipKeyPair{}
for _, pair := range groupKeys {
idKey, err := hex.DecodeString(pair[0])
if err != nil {
return nil, err
}
idCommitment, err := hex.DecodeString(pair[1])
if err != nil {
return nil, err
}
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,
})
groupKeyPairs = append(groupKeyPairs, MembershipKeyPair{IDKey: IDKey(Bytes32(idKey)), IDCommitment: IDCommitment(Bytes32(idCommitment))})
}
return groupIdCredentials, nil
return groupKeyPairs, nil
}
func Bytes32(b []byte) [32]byte {
@ -61,116 +34,3 @@ func Bytes128(b []byte) [128]byte {
copy(result[128-len(b):], b)
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
}

View File

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

32
scripts/Cross.toml Normal file
View File

@ -0,0 +1,32 @@
[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"

53
scripts/build.sh Executable file
View File

@ -0,0 +1,53 @@
#!/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
zerokit Submodule

@ -0,0 +1 @@
Subproject commit afa4a09bba1c1d754bc16a069541259e72be4aa0