From fbbf5fc6461c0e34e965f995b423873edd05a0fb Mon Sep 17 00:00:00 2001 From: Kolezhniuk Date: Mon, 17 Nov 2025 10:38:23 +0800 Subject: [PATCH 1/6] Add trusted setup v3 circuits --- circuits.go | 97 +++++++++ credentialAtomicQueryV3Universal _test.go | 162 +++++++++++++++ credentialAtomicQueryV3Universal.go | 241 ++++++++++++++++++++++ linkedMultiQuery.go | 51 +++-- linkedMultiQuery_test.go | 123 +++++++---- 5 files changed, 610 insertions(+), 64 deletions(-) create mode 100644 credentialAtomicQueryV3Universal _test.go create mode 100644 credentialAtomicQueryV3Universal.go diff --git a/circuits.go b/circuits.go index b5570ac..d35926e 100644 --- a/circuits.go +++ b/circuits.go @@ -50,6 +50,18 @@ const ( SybilSigCircuitID CircuitID = "sybilCredentialAtomicSig" // LinkedMultiQuery10CircuitID is a type for linkedMultiQuery10.circom LinkedMultiQuery10CircuitID CircuitID = "linkedMultiQuery10-beta.1" + + AtomicQueryV3StableCircuitID CircuitID = "credentialAtomicQueryV3" + AtomicQueryV3Stable_16_16_64CircuitID CircuitID = "credentialAtomicQueryV3-16-16-64" + AtomicQueryV3OnChainStableCircuitID CircuitID = "credentialAtomicQueryV3OnChain" + AtomicQueryV3OnChainStable_16_16_64_16_32CircuitID CircuitID = "credentialAtomicQueryV3OnChain-16-16-64-16-32" + + AtomicQueryV3UniversalCircuitID CircuitID = "credentialAtomicQueryV3Universal" + AtomicQueryV3Universal_16_16_64CircuitID CircuitID = "credentialAtomicQueryV3Universal-16-16-64" + + LinkedMultiQuery3CircuitID CircuitID = "linkedMultiQuery3" + LinkedMultiQuery5CircuitID CircuitID = "linkedMultiQuery5" + LinkedMultiQuery10V2CircuitID CircuitID = "linkedMultiQuery10" ) // ErrorCircuitIDNotFound returns if CircuitID is not registered @@ -141,6 +153,91 @@ func init() { Input: AtomicQueryV3OnChainInputs{}, Output: &AtomicQueryV3OnChainPubSignals{}, }) + + RegisterCircuit(AtomicQueryV3StableCircuitID, Data{ + Input: AtomicQueryV3Inputs{}, + Output: &AtomicQueryV3PubSignals{}, + }) + + RegisterCircuit(AtomicQueryV3OnChainStableCircuitID, Data{ + Input: AtomicQueryV3OnChainInputs{}, + Output: &AtomicQueryV3OnChainPubSignals{}, + }) + + RegisterCircuit(AtomicQueryV3UniversalCircuitID, Data{ + Input: AtomicQueryV3Inputs{}, + Output: &AtomicQueryV3UniversalPubSignals{}, + }) + + baseConfig := BaseConfig{ + MTLevel: 16, + MTLevelClaim: 16, + MTLevelOnChain: 32, + ValueArraySize: 64, + } + + RegisterCircuit(AtomicQueryV3Stable_16_16_64CircuitID, Data{ + Input: AtomicQueryV3Inputs{ + BaseConfig: baseConfig, + }, + Output: &AtomicQueryV3PubSignals{ + BaseConfig: baseConfig, + }, + }) + + RegisterCircuit(AtomicQueryV3OnChainStable_16_16_64_16_32CircuitID, Data{ + Input: AtomicQueryV3OnChainInputs{ + BaseConfig: baseConfig, + }, + Output: &AtomicQueryV3OnChainPubSignals{ + BaseConfig: baseConfig, + }, + }) + + RegisterCircuit(AtomicQueryV3Universal_16_16_64CircuitID, Data{ + Input: AtomicQueryV3Inputs{ + BaseConfig: baseConfig, + }, + Output: &AtomicQueryV3UniversalPubSignals{ + BaseConfig: baseConfig, + }, + }) + + RegisterCircuit(LinkedMultiQuery10CircuitID, Data{ + Input: LinkedMultiQueryInputs{ + queryLength: LinkedMultiQueryLength, + }, + Output: &LinkedMultiQueryPubSignals{ + queryLength: LinkedMultiQueryLength, + }, + }) + + RegisterCircuit(LinkedMultiQuery10V2CircuitID, Data{ + Input: LinkedMultiQueryInputs{ + queryLength: LinkedMultiQueryLength, + }, + Output: &LinkedMultiQueryPubSignals{ + queryLength: LinkedMultiQueryLength, + }, + }) + + RegisterCircuit(LinkedMultiQuery3CircuitID, Data{ + Input: LinkedMultiQueryInputs{ + queryLength: 3, + }, + Output: &LinkedMultiQueryPubSignals{ + queryLength: 3, + }, + }) + + RegisterCircuit(LinkedMultiQuery5CircuitID, Data{ + Input: LinkedMultiQueryInputs{ + queryLength: 5, + }, + Output: &LinkedMultiQueryPubSignals{ + queryLength: 5, + }, + }) } // BaseConfig base circuit's config, provides default configuration for default circuits diff --git a/credentialAtomicQueryV3Universal _test.go b/credentialAtomicQueryV3Universal _test.go new file mode 100644 index 0000000..0b2b98f --- /dev/null +++ b/credentialAtomicQueryV3Universal _test.go @@ -0,0 +1,162 @@ +package circuits + +import ( + "encoding/json" + "math/big" + "testing" + + it "github.com/iden3/go-circuits/v2/testing" + "github.com/stretchr/testify/require" +) + +func TestAtomicQueryV3UniversalOutputsCircuitUnmarshal(t *testing.T) { + out := new(AtomicQueryV3UniversalPubSignals) + err := out.PubSignalsUnmarshal([]byte( + `[ + "22547885961380641656890522948966953732133055194604876766672713705832321537", + "19807516177872076324802462820131296019241193150904977369555677697559725431535", + "4575193482325603893215142619623809490166676718476384166883471046672443708329", + "0", + "16321897390546343714174413659582254042752392145999028505097676701328201511519", + "0", + "2", + "23", + "22057981499787921734624217749308316644136637822444794206796063681866502657", + "1", + "4575193482325603893215142619623809490166676718476384166883471046672443708329", + "1642074362", + "180410020913331409885634153623124536270", + "0", + "2", + "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", + "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", + "0", + "0", + "0", + "0", + "21929109382993718606847853573861987353620810345503358891473103689157378049", + "123" +]`)) + require.NoError(t, err) + + expValue, err := PrepareCircuitArrayValues([]*big.Int{}, 64) + require.NoError(t, err) + + circuitQueryHash, ok := big.NewInt(0).SetString("19807516177872076324802462820131296019241193150904977369555677697559725431535", 10) + require.True(t, ok) + + nullifier, ok := big.NewInt(0).SetString("16321897390546343714174413659582254042752392145999028505097676701328201511519", 10) + require.True(t, ok) + + exp := AtomicQueryV3UniversalPubSignals{ + RequestID: big.NewInt(23), + CircuitQueryHash: circuitQueryHash, + UserID: it.IDFromStr(t, + "22547885961380641656890522948966953732133055194604876766672713705832321537"), + IssuerID: it.IDFromStr(t, + "22057981499787921734624217749308316644136637822444794206796063681866502657"), + IssuerState: it.MTHashFromStr(t, + "4575193482325603893215142619623809490166676718476384166883471046672443708329"), + IssuerClaimNonRevState: it.MTHashFromStr(t, + "4575193482325603893215142619623809490166676718476384166883471046672443708329"), + ClaimSchema: it.CoreSchemaFromStr(t, + "180410020913331409885634153623124536270"), + SlotIndex: 2, + Operator: 0, + Value: expValue, + ActualValueArraySize: 0, + Timestamp: int64(1642074362), + Merklized: 0, + ClaimPathKey: big.NewInt(0), + IsRevocationChecked: 1, + ProofType: 2, + LinkID: big.NewInt(0), + Nullifier: nullifier, + OperatorOutput: big.NewInt(0), + VerifierID: it.IDFromStr( + t, "21929109382993718606847853573861987353620810345503358891473103689157378049"), + NullifierSessionID: big.NewInt(123), + } + + 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)) + + statesInfo, err := exp.GetStatesInfo() + require.NoError(t, err) + wantStatesInfo := StatesInfo{ + States: []State{ + { + ID: idFromInt("22057981499787921734624217749308316644136637822444794206796063681866502657"), + State: hashFromInt("4575193482325603893215142619623809490166676718476384166883471046672443708329"), + }, + }, + Gists: []Gist{}, + } + j, err := json.Marshal(statesInfo) + require.NoError(t, err) + require.Equal(t, wantStatesInfo, statesInfo, string(j)) +} diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go new file mode 100644 index 0000000..8e41b9a --- /dev/null +++ b/credentialAtomicQueryV3Universal.go @@ -0,0 +1,241 @@ +package circuits + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" + + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-merkletree-sql/v2" + "github.com/pkg/errors" +) + +// AtomicQueryV3UniversalPubSignals public inputs +type AtomicQueryV3UniversalPubSignals struct { + BaseConfig + RequestID *big.Int `json:"requestID"` + UserID *core.ID `json:"userID"` + IssuerID *core.ID `json:"issuerID"` + IssuerState *merkletree.Hash `json:"issuerState"` + IssuerClaimNonRevState *merkletree.Hash `json:"issuerClaimNonRevState"` + ClaimSchema core.SchemaHash `json:"claimSchema"` + SlotIndex int `json:"slotIndex"` + Operator int `json:"operator"` + Value []*big.Int `json:"value"` + Timestamp int64 `json:"timestamp"` + Merklized int `json:"merklized"` + ClaimPathKey *big.Int `json:"claimPathKey"` + IsRevocationChecked int `json:"isRevocationChecked"` // 0 revocation not check, // 1 for check revocation + ProofType int `json:"proofType"` + LinkID *big.Int `json:"linkID"` + Nullifier *big.Int `json:"nullifier"` + OperatorOutput *big.Int `json:"operatorOutput"` + VerifierID *core.ID `json:"verifierID"` + NullifierSessionID *big.Int `json:"nullifierSessionID"` + ActualValueArraySize int `json:"valueArraySize"` + CircuitQueryHash *big.Int `json:"circuitQueryHash"` +} + +// PubSignalsUnmarshal unmarshal credentialAtomicQueryV3Universal.circom public signals +func (ao *AtomicQueryV3UniversalPubSignals) PubSignalsUnmarshal(data []byte) error { + // expected order: + // userID + // circuitQueryHash + // issuerState + // linkID + // nullifier + // operatorOutput + // proofType + // requestID + // issuerID + // isRevocationChecked + // issuerClaimNonRevState + // timestamp + // claimSchema + // claimPathKey + // slotIndex + // operator + // value + // valueArraySize + // verifierID + // nullifierSessionID + + // 19 is a number of fields in AtomicQueryV3UniversalPubSignals, values length could be + // different base on the circuit configuration. The length could be modified by set value + // in ValueArraySize + const fieldLength = 19 + + var sVals []string + err := json.Unmarshal(data, &sVals) + if err != nil { + return err + } + + if len(sVals) != fieldLength+ao.GetValueArrSize() { + return fmt.Errorf("invalid number of Output values expected {%d} go {%d} ", fieldLength+ao.GetValueArrSize(), len(sVals)) + } + + fieldIdx := 0 + + // - userID + if ao.UserID, err = idFromIntStr(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + var ok bool + // - circuitQueryHash + if ao.CircuitQueryHash, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid circuit query hash value: '%s'", sVals[fieldIdx]) + } + fieldIdx++ + + // - issuerState + if ao.IssuerState, err = merkletree.NewHashFromString(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - linkID + if ao.LinkID, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid link ID value: '%s'", sVals[fieldIdx]) + } + fieldIdx++ + + // - nullifier + if ao.Nullifier, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid link ID value: '%s'", sVals[fieldIdx]) + } + fieldIdx++ + + // - operatorOutput + if ao.OperatorOutput, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid operator output value: '%s'", sVals[fieldIdx]) + } + fieldIdx++ + + if ao.ProofType, err = strconv.Atoi(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - requestID + if ao.RequestID, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid requestID value: '%s'", sVals[fieldIdx]) + } + fieldIdx++ + + // - issuerID + if ao.IssuerID, err = idFromIntStr(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - isRevocationChecked + if ao.IsRevocationChecked, err = strconv.Atoi(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - issuerClaimNonRevState + if ao.IssuerClaimNonRevState, err = merkletree.NewHashFromString(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - timestamp + ao.Timestamp, err = strconv.ParseInt(sVals[fieldIdx], 10, 64) + if err != nil { + return err + } + fieldIdx++ + + // - claimSchema + var schemaInt *big.Int + if schemaInt, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid schema value: '%s'", sVals[0]) + } + ao.ClaimSchema = core.NewSchemaHashFromInt(schemaInt) + fieldIdx++ + + // - ClaimPathKey + if ao.ClaimPathKey, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid claimPathKey: %s", sVals[fieldIdx]) + } + fieldIdx++ + + // - slotIndex + if ao.SlotIndex, err = strconv.Atoi(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - operator + if ao.Operator, err = strconv.Atoi(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - values + var valuesNum = ao.GetValueArrSize() + for i := 0; i < valuesNum; i++ { + bi, ok := big.NewInt(0).SetString(sVals[fieldIdx], 10) + if !ok { + return fmt.Errorf("invalid value in index: %d", i) + } + ao.Value = append(ao.Value, bi) + fieldIdx++ + } + + // - valueArraySize + if ao.ActualValueArraySize, err = strconv.Atoi(sVals[fieldIdx]); err != nil { + return err + } + fieldIdx++ + + // - VerifierID + if sVals[fieldIdx] != "0" { + if ao.VerifierID, err = idFromIntStr(sVals[fieldIdx]); err != nil { + return err + } + } + fieldIdx++ + + // - NullifierSessionID + if ao.NullifierSessionID, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { + return fmt.Errorf("invalid verifier session ID: %s", sVals[fieldIdx]) + } + + return nil +} + +// GetObjMap returns struct field as a map +func (ao AtomicQueryV3UniversalPubSignals) GetObjMap() map[string]interface{} { + return toMap(ao) +} + +func (ao AtomicQueryV3UniversalPubSignals) GetStatesInfo() (StatesInfo, error) { + if ao.IssuerID == nil { + return StatesInfo{}, errors.New(ErrorEmptyID) + } + + if ao.IssuerState == nil || ao.IssuerClaimNonRevState == nil { + return StatesInfo{}, errors.New(ErrorEmptyStateHash) + } + + states := []State{ + { + ID: *ao.IssuerID, + State: *ao.IssuerState, + }, + } + if *ao.IssuerClaimNonRevState != *ao.IssuerState { + states = append(states, State{ + ID: *ao.IssuerID, + State: *ao.IssuerClaimNonRevState, + }) + } + + return StatesInfo{States: states, Gists: []Gist{}}, nil +} diff --git a/linkedMultiQuery.go b/linkedMultiQuery.go index 9dad57c..ed7102c 100644 --- a/linkedMultiQuery.go +++ b/linkedMultiQuery.go @@ -16,9 +16,10 @@ const LinkedMultiQueryLength = 10 // LinkedMultiQueryInputs type represent linkedMultiQuery10.circom inputs type LinkedMultiQueryInputs struct { BaseConfig - LinkNonce *big.Int - Claim *core.Claim - Query []*Query + queryLength int + LinkNonce *big.Int + Claim *core.Claim + Query []*Query } // linkedMultiQueryCircuitInputs type reflect linkedMultiQuery10.circom private inputs required by prover @@ -51,7 +52,7 @@ func (l LinkedMultiQueryInputs) Validate() error { return errors.New(ErrorEmptyQueries) } - if len(l.Query) > LinkedMultiQueryLength { + if len(l.Query) > l.queryLength { return errors.New(ErrorTooManyQueries) } @@ -69,6 +70,10 @@ func (l LinkedMultiQueryInputs) Validate() error { // InputsMarshal returns Circom private inputs for linkedMultiQuery10.circom func (l LinkedMultiQueryInputs) InputsMarshal() ([]byte, error) { + if l.queryLength == 0 { + l.queryLength = LinkedMultiQueryLength + } + if err := l.Validate(); err != nil { return nil, err } @@ -78,18 +83,17 @@ func (l LinkedMultiQueryInputs) InputsMarshal() ([]byte, error) { s.IssuerClaim = l.Claim s.ClaimSchema = l.Claim.GetSchemaHash().BigInt().String() - 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) - s.ActualValueArraySize = make([]int, LinkedMultiQueryLength) - - for i := 0; i < LinkedMultiQueryLength; i++ { + s.ClaimPathMtp = make([][]string, l.queryLength) + s.ClaimPathMtpNoAux = make([]string, l.queryLength) + s.ClaimPathMtpAuxHi = make([]*merkletree.Hash, l.queryLength) + s.ClaimPathMtpAuxHv = make([]*merkletree.Hash, l.queryLength) + s.ClaimPathKey = make([]string, l.queryLength) + s.ClaimPathValue = make([]string, l.queryLength) + s.SlotIndex = make([]int, l.queryLength) + s.Operator = make([]int, l.queryLength) + s.Value = make([][]string, l.queryLength) + s.ActualValueArraySize = make([]int, l.queryLength) + for i := 0; i < l.queryLength; i++ { if i >= len(l.Query) || l.Query[i] == nil { s.ClaimPathMtp[i] = PrepareSiblingsStr([]*merkletree.Hash{}, l.GetMTLevelsClaim()) @@ -150,6 +154,7 @@ type LinkedMultiQueryPubSignals struct { Merklized int `json:"merklized"` OperatorOutput []*big.Int `json:"operatorOutput"` CircuitQueryHash []*big.Int `json:"circuitQueryHash"` + queryLength int } // PubSignalsUnmarshal unmarshal linkedMultiQuery10.circom public inputs to LinkedMultiQueryPubSignals @@ -160,7 +165,11 @@ func (lo *LinkedMultiQueryPubSignals) PubSignalsUnmarshal(data []byte) error { // operatorOutput // circuitQueryHash - outputsLength := LinkedMultiQueryLength*2 + 2 + if lo.queryLength == 0 { + lo.queryLength = LinkedMultiQueryLength + } + + outputsLength := lo.queryLength*2 + 2 var sVals []string err := json.Unmarshal(data, &sVals) if err != nil { @@ -187,16 +196,16 @@ func (lo *LinkedMultiQueryPubSignals) PubSignalsUnmarshal(data []byte) error { fieldIdx++ // -- operatorOutput - lo.OperatorOutput = make([]*big.Int, LinkedMultiQueryLength) - for i := 0; i < LinkedMultiQueryLength; i++ { + lo.OperatorOutput = make([]*big.Int, lo.queryLength) + for i := 0; i < lo.queryLength; 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++ { + lo.CircuitQueryHash = make([]*big.Int, lo.queryLength) + for i := 0; i < lo.queryLength; i++ { if lo.CircuitQueryHash[i], ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { return fmt.Errorf("invalid query hash value: '%s'", sVals[fieldIdx]) } diff --git a/linkedMultiQuery_test.go b/linkedMultiQuery_test.go index 71e9dad..6e4df07 100644 --- a/linkedMultiQuery_test.go +++ b/linkedMultiQuery_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "math/big" + "strconv" "testing" it "github.com/iden3/go-circuits/v2/testing" @@ -137,60 +138,96 @@ func TestLinkedMultiQueryInputs_PrepareInputs_Error(t *testing.T) { } func TestLinkedMultiQueryPubSignals_CircuitUnmarshal(t *testing.T) { - out := new(LinkedMultiQueryPubSignals) - err := out.PubSignalsUnmarshal([]byte( - `[ - "443", - "1", + outs := map[int][]string{ + 3: { + "11587660915189382633314527098062647837126752531205087409048618395969242885016", + "0", + "0", + "0", + "0", + "9458417390459068300741864705379630488534450155484493792325907355745201035449", + "10864698602219511323750171112812294233505545576258213541845435681330532958075", + "5365138871441717895206514697230448654236988235704905467582456422975445794731", + }, + 5: { + "20336008450539684768013573494073798243349685857640613070314041678185349736439", "1", - "2", - "3", - "4", - "5", "0", "0", "0", "0", "0", - "100", - "200", - "300", - "400", - "500", + "3326382892536126749483088946048689911243394580824744244053752370464747528203", + "9907132056133666096701539062450765284880813426582692863734448403438789333698", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + }, + 10: { + "11587660915189382633314527098062647837126752531205087409048618395969242885016", "0", "0", "0", "0", - "0" - ]`)) - require.NoError(t, err) - - operatorOutput := make([]*big.Int, 10) - circuitQueryHash := make([]*big.Int, 10) - valueArrSize := make([]int, 10) - for i := 1; i <= 10; i++ { - indx := i - 1 - operatorOutput[indx] = big.NewInt((int64(i))) - circuitQueryHash[indx] = big.NewInt(int64(i * 100)) - valueArrSize[indx] = 1 - if i > 5 { - operatorOutput[indx] = big.NewInt(0) - circuitQueryHash[indx] = big.NewInt(0) - valueArrSize[indx] = 0 - } + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "9458417390459068300741864705379630488534450155484493792325907355745201035449", + "10864698602219511323750171112812294233505545576258213541845435681330532958075", + "5365138871441717895206514697230448654236988235704905467582456422975445794731", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + "6552534440600411908158043655342660449140617599402291128616319085888035740680", + }, } - exp := LinkedMultiQueryPubSignals{ - LinkID: big.NewInt(443), - Merklized: 1, - OperatorOutput: operatorOutput, - CircuitQueryHash: circuitQueryHash, + for queriesCount, out := range outs { + t.Run(fmt.Sprintf("LinkedMultiQueryPubSignals_CircuitUnmarshal_%d", queriesCount), func(t *testing.T) { + ao := &LinkedMultiQueryPubSignals{ + queryLength: queriesCount, + } + + jsonData, err := json.Marshal(out) + require.NoError(t, err) + + err = ao.PubSignalsUnmarshal(jsonData) + require.NoError(t, err) + + // Check linkID (out[0]) + expectedLinkID, ok := big.NewInt(0).SetString(out[0], 10) + require.True(t, ok, "failed to parse linkID") + require.Equal(t, expectedLinkID, ao.LinkID) + + // Check merklized (out[1]) + expectedMerklized, err := strconv.Atoi(out[1]) + require.NoError(t, err) + require.Equal(t, expectedMerklized, ao.Merklized) + + // Check operatorOutput (out.slice(2, 2 + queriesCount)) + expectedOperatorOutput := make([]*big.Int, queriesCount) + for i := 0; i < queriesCount; i++ { + val, ok := big.NewInt(0).SetString(out[2+i], 10) + require.True(t, ok, fmt.Sprintf("failed to parse operatorOutput[%d]", i)) + expectedOperatorOutput[i] = val + } + require.Equal(t, expectedOperatorOutput, ao.OperatorOutput) + + // Check circuitQueryHash (out.slice(2 + queriesCount, 2 + queriesCount * 2)) + expectedCircuitQueryHash := make([]*big.Int, queriesCount) + for i := 0; i < queriesCount; i++ { + val, ok := big.NewInt(0).SetString(out[2+queriesCount+i], 10) + require.True(t, ok, fmt.Sprintf("failed to parse circuitQueryHash[%d]", i)) + expectedCircuitQueryHash[i] = val + } + require.Equal(t, expectedCircuitQueryHash, ao.CircuitQueryHash) + }) } - - 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)) } From bcdad3762f403752a7dd43e1964866af49459e0e Mon Sep 17 00:00:00 2001 From: Dimasik Date: Mon, 17 Nov 2025 15:29:54 +0800 Subject: [PATCH 2/6] Update credentialAtomicQueryV3Universal.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- credentialAtomicQueryV3Universal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go index 8e41b9a..8da8ba6 100644 --- a/credentialAtomicQueryV3Universal.go +++ b/credentialAtomicQueryV3Universal.go @@ -105,7 +105,7 @@ func (ao *AtomicQueryV3UniversalPubSignals) PubSignalsUnmarshal(data []byte) err // - nullifier if ao.Nullifier, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { - return fmt.Errorf("invalid link ID value: '%s'", sVals[fieldIdx]) + return fmt.Errorf("invalid nullifier value: '%s'", sVals[fieldIdx]) } fieldIdx++ From ba74db16cd4ce74dec567cc4885631e1b19bee41 Mon Sep 17 00:00:00 2001 From: Dimasik Date: Mon, 17 Nov 2025 15:30:07 +0800 Subject: [PATCH 3/6] Update credentialAtomicQueryV3Universal.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- credentialAtomicQueryV3Universal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go index 8da8ba6..5d89fa5 100644 --- a/credentialAtomicQueryV3Universal.go +++ b/credentialAtomicQueryV3Universal.go @@ -154,7 +154,7 @@ func (ao *AtomicQueryV3UniversalPubSignals) PubSignalsUnmarshal(data []byte) err // - claimSchema var schemaInt *big.Int if schemaInt, ok = big.NewInt(0).SetString(sVals[fieldIdx], 10); !ok { - return fmt.Errorf("invalid schema value: '%s'", sVals[0]) + return fmt.Errorf("invalid schema value: '%s'", sVals[fieldIdx]) } ao.ClaimSchema = core.NewSchemaHashFromInt(schemaInt) fieldIdx++ From 469ea6ae3d6a19ffff4fdfa98fb66a7ced0c9824 Mon Sep 17 00:00:00 2001 From: Dimasik Date: Mon, 17 Nov 2025 15:30:15 +0800 Subject: [PATCH 4/6] Update credentialAtomicQueryV3Universal.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- credentialAtomicQueryV3Universal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go index 5d89fa5..448cb83 100644 --- a/credentialAtomicQueryV3Universal.go +++ b/credentialAtomicQueryV3Universal.go @@ -73,7 +73,7 @@ func (ao *AtomicQueryV3UniversalPubSignals) PubSignalsUnmarshal(data []byte) err } if len(sVals) != fieldLength+ao.GetValueArrSize() { - return fmt.Errorf("invalid number of Output values expected {%d} go {%d} ", fieldLength+ao.GetValueArrSize(), len(sVals)) + return fmt.Errorf("invalid number of Output values expected {%d} got {%d} ", fieldLength+ao.GetValueArrSize(), len(sVals)) } fieldIdx := 0 From 26c101ce393a50a98c96e120a28050262b069e4e Mon Sep 17 00:00:00 2001 From: Dimasik Date: Mon, 17 Nov 2025 15:30:33 +0800 Subject: [PATCH 5/6] Update credentialAtomicQueryV3Universal.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- credentialAtomicQueryV3Universal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go index 448cb83..f91214e 100644 --- a/credentialAtomicQueryV3Universal.go +++ b/credentialAtomicQueryV3Universal.go @@ -26,7 +26,7 @@ type AtomicQueryV3UniversalPubSignals struct { Timestamp int64 `json:"timestamp"` Merklized int `json:"merklized"` ClaimPathKey *big.Int `json:"claimPathKey"` - IsRevocationChecked int `json:"isRevocationChecked"` // 0 revocation not check, // 1 for check revocation + IsRevocationChecked int `json:"isRevocationChecked"` // 0 for no revocation check, 1 for revocation check ProofType int `json:"proofType"` LinkID *big.Int `json:"linkID"` Nullifier *big.Int `json:"nullifier"` From a97891713eb1127f75003bc736266448a18cb753 Mon Sep 17 00:00:00 2001 From: Dimasik Date: Mon, 17 Nov 2025 15:30:51 +0800 Subject: [PATCH 6/6] Update credentialAtomicQueryV3Universal.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- credentialAtomicQueryV3Universal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentialAtomicQueryV3Universal.go b/credentialAtomicQueryV3Universal.go index f91214e..aeb4c96 100644 --- a/credentialAtomicQueryV3Universal.go +++ b/credentialAtomicQueryV3Universal.go @@ -62,7 +62,7 @@ func (ao *AtomicQueryV3UniversalPubSignals) PubSignalsUnmarshal(data []byte) err // nullifierSessionID // 19 is a number of fields in AtomicQueryV3UniversalPubSignals, values length could be - // different base on the circuit configuration. The length could be modified by set value + // different based on the circuit configuration. The length could be modified by set value // in ValueArraySize const fieldLength = 19