Wrap proof with custom witness

This commit is contained in:
alrevuelta 2024-01-17 13:29:26 +01:00
parent 7e086e8f89
commit 4c93f5564f
No known key found for this signature in database
GPG Key ID: F345C9F3CCDB886E
12 changed files with 296 additions and 2 deletions

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.19
require (
github.com/stretchr/testify v1.7.2
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117094748-68b4162e8fd7
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240116134931-a8b8c6ab4b80
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240116135046-2875fec12afc
)

6
go.sum
View File

@ -21,6 +21,12 @@ github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116121347-ee5a1d931442 h1:x
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116121347-ee5a1d931442/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef h1:MAkZryAeRhiH3TKHRK2h+WztZI1VqfQ/oeXMIxKZNy0=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117093624-7ba5a338d490 h1:1OivqdMCBCRIp2qzUggSpZPhcaRkcFl0U5UoPfGWB9g=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117093624-7ba5a338d490/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117093914-f79c87b06466 h1:Baxd/BPVGKgaVvLoI0//UtSVNovlY/IOROQpfni8pbE=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117093914-f79c87b06466/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117094748-68b4162e8fd7 h1:MOpAZITkW2EkI7aO1uUGWsGuUnQH3K/Mk7WuqLpFQGo=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240117094748-68b4162e8fd7/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 h1:Sd7QD/1Yo2o2M1MY49F8Zr4KNBPUEK5cz5HoXQVJbrs=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240116134931-a8b8c6ab4b80 h1:3KObRaYJnTI41U0reNBk7DYr5PVCTq8T9gJLXGfPfaY=

View File

@ -91,6 +91,10 @@ 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)
}

View File

@ -90,6 +90,10 @@ 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)
}

View File

@ -91,6 +91,10 @@ 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)
}

View File

@ -233,6 +233,64 @@ func (r *RLN) GenerateProof(data []byte, key IdentityCredential, index Membershi
}, nil
}
// input :
/*
identity_secret: Fr,
path_elements: Vec<Fr>,
identity_path_index: Vec<u8>,
x: Fr,
epoch: Fr,
rln_identifier: Fr,
*/
// todo: output same as other function.
// output [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {
proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
if err != nil {
return nil, err
}
if len(proofBytes) != 320 {
return nil, errors.New("invalid proof generated")
}
// TODO: maybe move this into a common function (used by the other generateRlnproof function)
// 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
epochOffset := rootOffset + 32
shareXOffset := epochOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
rlnIdentifierOffset := nullifierOffset + 32
var zkproof ZKSNARK
var proofRoot, shareX, shareY MerkleNode
var epochR Epoch
var nullifier Nullifier
var rlnIdentifier RLNIdentifier
copy(zkproof[:], proofBytes[0:proofOffset])
copy(proofRoot[:], proofBytes[proofOffset:rootOffset])
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,
Epoch: epochR,
ShareX: shareX,
ShareY: shareY,
Nullifier: nullifier,
RLNIdentifier: rlnIdentifier,
}, nil
}
func serialize32(roots [][32]byte) []byte {
var result []byte
for _, r := range roots {
@ -406,6 +464,8 @@ func (r *RLN) GetMerkleProof(index MembershipIndex) (MerkleProof, error) {
return MerkleProof{}, err
}
// TODO: Take this from the function
// Check if we can read the first byte
if len(proofBytes) < 8 {
return MerkleProof{}, errors.New(fmt.Sprintf("wrong output size: %d", len(proofBytes)))

View File

@ -350,6 +350,64 @@ func (s *RLNSuite) TestGetMerkleProof() {
}
}
func (s *RLNSuite) TestGenerateRLNProofWithWitness() {
rln, err := NewRLN()
s.NoError(err)
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 := 0; i < 10; i++ {
if i == index {
// insert the current peer's pk
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)
s.NoError(err)
}
}
root, err := rln.GetMerkleRoot()
s.NoError(err)
// prepare the message
msg := []byte("Hello")
// prepare the epoch
var epoch Epoch
badIndex := 4
merkleProof, err := rln.GetMerkleProof(uint(badIndex))
s.NoError(err)
rlnWitness := RLNWitnessInput{
IdentityCredential: *memKeys,
MerkleProof: merkleProof,
Data: msg,
Epoch: epoch,
RlnIdentifier: [32]byte{0x00, 0x00, 0x00}, // TODO
}
// generate proof
proofRes, err := rln.GenerateRLNProofWithWitness(rlnWitness)
s.NoError(err)
// verify the proof (should not be verified)
verified, err := rln.Verify(msg, *proofRes, root)
s.NoError(err)
s.False(verified)
}
func (s *RLNSuite) TestEpochConsistency() {
// check edge cases
var epoch uint64 = math.MaxUint64

View File

@ -1,6 +1,11 @@
package rln
import "encoding/binary"
import (
"encoding/binary"
"errors"
"fmt"
"math/big"
)
// serialize converts a RateLimitProof and the data to a byte seq
// this conversion is used in the proofGen function
@ -42,3 +47,82 @@ func (r RateLimitProof) serialize() []byte {
proofBytes = append(proofBytes, r.RLNIdentifier[:]...)
return proofBytes
}
func (r *RLNWitnessInput) serialize() []byte {
output := make([]byte, 0)
output = append(output, r.IdentityCredential.IDSecretHash[:]...)
output = append(output, r.MerkleProof.serialize()...)
output = append(output, appendLength(r.Data)...)
output = append(output, r.Epoch[:]...)
output = append(output, r.RlnIdentifier[:]...)
return output
}
func (r *MerkleProof) serialize() []byte {
output := make([]byte, 0)
output = append(output, appendLength(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 output 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 output 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()))
}
// TODO: Depth check, but currently not accesible
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
}

56
rln/serialize_test.go Normal file
View File

@ -0,0 +1,56 @@
package rln
import (
"fmt"
"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) {
mProof := MerkleProof{
PathElements: []MerkleNode{},
PathIndexes: []uint8{},
}
ser := mProof.serialize()
//require.Equal(t, []byte{0, 0, 0, 0}, ser, )
require.Equal(t, 16, len(ser))
mProof = MerkleProof{
PathElements: []MerkleNode{[32]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}},
PathIndexes: []uint8{0},
}
ser = mProof.serialize()
//require.Equal(t, []byte{0, 0, 0, 0}, ser, )
require.Equal(t, 49, len(ser))
mProof = MerkleProof{}
for i := 0; i < 16; i++ {
mProof.PathElements = append(mProof.PathElements, random32())
mProof.PathIndexes = append(mProof.PathIndexes, uint8(i%2))
}
ser = mProof.serialize()
fmt.Println(ser)
desProof := MerkleProof{}
err := desProof.deserialize(ser)
require.NoError(t, err)
// TODO test for errors. eg different size.
}
func TestRLNWitnessInputSerDe(t *testing.T) {
}

View File

@ -68,6 +68,14 @@ type MerkleProof struct {
PathIndexes []uint8 `json:"pathIndexes"`
}
type RLNWitnessInput struct {
IdentityCredential IdentityCredential `json:"identityCredential"`
MerkleProof MerkleProof `json:"merkleProof"`
Data []byte `json:"data"`
Epoch Epoch `json:"epoch"`
RlnIdentifier RLNIdentifier `json:"rlnIdentifier"` // what is this? TOOD: app specific. which one is ours?
}
type TreeDepth int
const (

View File

@ -56,6 +56,14 @@ func Bytes128(b []byte) [128]byte {
return result
}
func Flatten(b [][32]byte) []byte {
var result []byte
for _, v := range b {
result = append(result, v[:]...)
}
return result
}
func ToBytes32LE(hexStr string) ([32]byte, error) {
b, err := hex.DecodeString(hexStr)

View File

@ -19,3 +19,5 @@ func TestBigInt(t *testing.T) {
newValue := Bytes32ToBigInt(b32Value)
require.True(t, bytes.Equal(newValue.Bytes(), value.Bytes()))
}
// TODO: Test Flatten