Skip to content

Commit

Permalink
Feature/linked circuits (#78)
Browse files Browse the repository at this point in the history
* add linked circuits wrappers
  • Loading branch information
volodymyr-basiuk authored Feb 16, 2024
1 parent 8450ece commit b0a9eb4
Show file tree
Hide file tree
Showing 4 changed files with 1,446 additions and 0 deletions.
2 changes: 2 additions & 0 deletions circuits.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const (
SybilMTPCircuitID CircuitID = "sybilCredentialAtomicMTP"
// SybilSigCircuitID is a type for sybilSig.circom
SybilSigCircuitID CircuitID = "sybilCredentialAtomicSig"
// LinkedMultiQuery10CircuitID is a type for linkedMultiQuery10.circom
LinkedMultiQuery10CircuitID CircuitID = "linkedMultiQuery10-beta.0"
)

// ErrorCircuitIDNotFound returns if CircuitID is not registered
Expand Down
195 changes: 195 additions & 0 deletions linkedMultiQuery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package circuits

import (
"encoding/json"
"fmt"
"math/big"
"strconv"

core "github.com/iden3/go-iden3-core/v2"
"github.com/iden3/go-merkletree-sql/v2"
)

const LinkedMultiQueryLength = 10

// LinkedMultiQueryInputs type represent linkedMultiQuery10.circom inputs
type LinkedMultiQueryInputs struct {
BaseConfig
LinkNonce *big.Int
Claim *core.Claim
Query []*Query
}

// linkedMultiQueryCircuitInputs type reflect linkedMultiQuery10.circom private inputs required by prover
type linkedMultiQueryCircuitInputs struct {
LinkNonce string `json:"linkNonce"`
IssuerClaim *core.Claim `json:"issuerClaim"`
Enabled []int `json:"enabled"`
ClaimSchema string `json:"claimSchema"`
ClaimPathNotExists []int `json:"claimPathNotExists"` // 0 for inclusion, 1 for non-inclusion
ClaimPathMtp [][]string `json:"claimPathMtp"`
ClaimPathMtpNoAux []string `json:"claimPathMtpNoAux"` // 1 if aux node is empty, 0 if non-empty or for inclusion proofs
ClaimPathMtpAuxHi []*merkletree.Hash `json:"claimPathMtpAuxHi"` // 0 for inclusion proof
ClaimPathMtpAuxHv []*merkletree.Hash `json:"claimPathMtpAuxHv"` // 0 for inclusion proof
ClaimPathKey []string `json:"claimPathKey"` // hash of path in merklized json-ld document
ClaimPathValue []string `json:"claimPathValue"` // value in this path in merklized json-ld document
SlotIndex []int `json:"slotIndex"`
Operator []int `json:"operator"`
Value [][]string `json:"value"`
}

// InputsMarshal returns Circom private inputs for linkedMultiQuery10.circom
func (l LinkedMultiQueryInputs) InputsMarshal() ([]byte, error) {
s := linkedMultiQueryCircuitInputs{}
s.LinkNonce = l.LinkNonce.String()
s.IssuerClaim = l.Claim
s.ClaimSchema = l.Claim.GetSchemaHash().BigInt().String()

s.Enabled = make([]int, LinkedMultiQueryLength)
s.ClaimPathNotExists = make([]int, LinkedMultiQueryLength)
s.ClaimPathMtp = make([][]string, LinkedMultiQueryLength)
s.ClaimPathMtpNoAux = make([]string, LinkedMultiQueryLength)
s.ClaimPathMtpAuxHi = make([]*merkletree.Hash, LinkedMultiQueryLength)
s.ClaimPathMtpAuxHv = make([]*merkletree.Hash, LinkedMultiQueryLength)
s.ClaimPathKey = make([]string, LinkedMultiQueryLength)
s.ClaimPathValue = make([]string, LinkedMultiQueryLength)
s.SlotIndex = make([]int, LinkedMultiQueryLength)
s.Operator = make([]int, LinkedMultiQueryLength)
s.Value = make([][]string, LinkedMultiQueryLength)

for i := 0; i < LinkedMultiQueryLength; i++ {
if l.Query[i] == nil {
s.Enabled[i] = 0
s.ClaimPathNotExists[i] = 0
s.ClaimPathMtp[i] = PrepareSiblingsStr([]*merkletree.Hash{}, l.GetMTLevelsClaim())

s.ClaimPathMtpNoAux[i] = "0"
s.ClaimPathMtpAuxHi[i] = &merkletree.HashZero
s.ClaimPathMtpAuxHv[i] = &merkletree.HashZero

s.ClaimPathKey[i] = "0"
s.ClaimPathValue[i] = "0"

s.SlotIndex[i] = 0
s.Operator[i] = 0

values, err := PrepareCircuitArrayValues(make([]*big.Int, 0), l.GetValueArrSize())
if err != nil {
return nil, err
}
s.Value[i] = bigIntArrayToStringArray(values)
continue
}

s.Enabled[i] = 1
valueProof := l.Query[i].ValueProof
if valueProof == nil {
valueProof = &ValueProof{}
valueProof.Path = big.NewInt(0)
valueProof.Value = big.NewInt(0)
valueProof.MTP = &merkletree.Proof{}
}

s.ClaimPathNotExists[i] = existenceToInt(valueProof.MTP.Existence)
s.ClaimPathMtp[i] = PrepareSiblingsStr(valueProof.MTP.AllSiblings(),
l.GetMTLevelsClaim())

nodAuxJSONLD := GetNodeAuxValue(valueProof.MTP)
s.ClaimPathMtpNoAux[i] = nodAuxJSONLD.noAux
s.ClaimPathMtpAuxHi[i] = nodAuxJSONLD.key
s.ClaimPathMtpAuxHv[i] = nodAuxJSONLD.value

s.ClaimPathKey[i] = valueProof.Path.String()
s.ClaimPathValue[i] = valueProof.Value.String()

s.SlotIndex[i] = l.Query[i].SlotIndex
s.Operator[i] = l.Query[i].Operator
values, err := PrepareCircuitArrayValues(l.Query[i].Values, l.GetValueArrSize())
if err != nil {
return nil, err
}
s.Value[i] = bigIntArrayToStringArray(values)
}

return json.Marshal(s)
}

// LinkedMultiQueryPubSignals linkedMultiQuery10.circom public signals
type LinkedMultiQueryPubSignals struct {
LinkID *big.Int `json:"linkID"`
Merklized int `json:"merklized"`
OperatorOutput []*big.Int `json:"operatorOutput"`
CircuitQueryHash []*big.Int `json:"circuitQueryHash"`
Enabled []bool `json:"enabled"`
}

// PubSignalsUnmarshal unmarshal linkedMultiQuery10.circom public inputs to LinkedMultiQueryPubSignals
func (lo *LinkedMultiQueryPubSignals) PubSignalsUnmarshal(data []byte) error {
// expected order:
// linkID
// merklized
// operatorOutput
// circuitQueryHash
// enabled

outputsLength := LinkedMultiQueryLength*3 + 2
var sVals []string
err := json.Unmarshal(data, &sVals)
if err != nil {
return err
}

if len(sVals) != outputsLength {
return fmt.Errorf("invalid number of Output values expected {%d} go {%d} ", outputsLength, len(sVals))
}

var ok bool
fieldIdx := 0

// - linkID
if lo.LinkID, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok {
return fmt.Errorf("invalid link ID value: '%s'", sVals[fieldIdx])
}
fieldIdx++

// -- merklized
if lo.Merklized, err = strconv.Atoi(sVals[fieldIdx]); err != nil {
return err
}
fieldIdx++

// -- operatorOutput
lo.OperatorOutput = make([]*big.Int, LinkedMultiQueryLength)
for i := 0; i < LinkedMultiQueryLength; i++ {
if lo.OperatorOutput[i], ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok {
return fmt.Errorf("invalid operator output value: '%s'", sVals[fieldIdx])
}
fieldIdx++
}
// -- circuitQueryHash
lo.CircuitQueryHash = make([]*big.Int, LinkedMultiQueryLength)
for i := 0; i < LinkedMultiQueryLength; i++ {
if lo.CircuitQueryHash[i], ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok {
return fmt.Errorf("invalid query hash value: '%s'", sVals[fieldIdx])
}
fieldIdx++
}

// -- enabled
lo.Enabled = make([]bool, LinkedMultiQueryLength)
for i := 0; i < LinkedMultiQueryLength; i++ {
enabledInt, err := strconv.Atoi(sVals[fieldIdx])
if err != nil {
return err
}
lo.Enabled[i] = enabledInt == 1
fieldIdx++
}

return nil
}

// GetObjMap returns LinkedMultiQueryPubSignals as a map
func (l LinkedMultiQueryPubSignals) GetObjMap() map[string]interface{} {
return toMap(l)
}
123 changes: 123 additions & 0 deletions linkedMultiQuery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package circuits

import (
"encoding/json"
"fmt"
"math/big"
"testing"

it "github.com/iden3/go-circuits/v2/testing"
"github.com/stretchr/testify/require"
)

func TestLinkedMultiQueryInputs_PrepareInputs(t *testing.T) {
user := it.NewIdentity(t, userPK)
subjectID := user.ID
claim := it.DefaultUserClaim(t, subjectID)

queries := make([]*Query, 10)
queries[0] = &Query{
ValueProof: nil,
Operator: EQ,
Values: it.PrepareIntArray([]*big.Int{big.NewInt(10)}, 64),
SlotIndex: 2,
}

queries[1] = &Query{
ValueProof: nil,
Operator: LT,
Values: it.PrepareIntArray([]*big.Int{big.NewInt(133)}, 64),
SlotIndex: 2,
}

queries[2] = &Query{
ValueProof: nil,
Operator: LTE,
Values: it.PrepareIntArray([]*big.Int{big.NewInt(555)}, 64),
SlotIndex: 2,
}

in := LinkedMultiQueryInputs{
LinkNonce: big.NewInt(35346346369657418),
Claim: claim,
Query: queries,
}

bytesInputs, err := in.InputsMarshal()
require.Nil(t, err)

fmt.Println(string(bytesInputs))

exp := it.TestData(t, "linkedMultiQuery_inputs", string(bytesInputs), *generate)
require.JSONEq(t, exp, string(bytesInputs))
}

func TestLinkedMultiQueryPubSignals_CircuitUnmarshal(t *testing.T) {
out := new(LinkedMultiQueryPubSignals)
err := out.PubSignalsUnmarshal([]byte(
`[
"443",
"1",
"1",
"2",
"3",
"4",
"5",
"0",
"0",
"0",
"0",
"0",
"100",
"200",
"300",
"400",
"500",
"0",
"0",
"0",
"0",
"0",
"1",
"1",
"1",
"1",
"1",
"0",
"0",
"0",
"0",
"0"
]`))
require.NoError(t, err)

operatorOutput := make([]*big.Int, 10)
circuitQueryHash := make([]*big.Int, 10)
enabled := make([]bool, 10)
for i := 1; i <= 10; i++ {
indx := i - 1
operatorOutput[indx] = big.NewInt((int64(i)))
circuitQueryHash[indx] = big.NewInt(int64(i * 100))
enabled[indx] = true
if i > 5 {
operatorOutput[indx] = big.NewInt(0)
circuitQueryHash[indx] = big.NewInt(0)
enabled[indx] = false
}
}

exp := LinkedMultiQueryPubSignals{
LinkID: big.NewInt(443),
Merklized: 1,
OperatorOutput: operatorOutput,
CircuitQueryHash: circuitQueryHash,
Enabled: enabled,
}

jsonOut, err := json.Marshal(out)
require.NoError(t, err)
jsonExp, err := json.Marshal(exp)
require.NoError(t, err)

require.JSONEq(t, string(jsonExp), string(jsonOut))
}
Loading

0 comments on commit b0a9eb4

Please sign in to comment.