@0xpolygonid/js-sdk
Version:
SDK to work with Polygon ID
168 lines (167 loc) • 6.48 kB
JavaScript
import { Hash, Proof, ZERO_HASH } from '@iden3/js-merkletree';
import { buildTreeState, isValidOperation, Operators, QueryOperators } from '../circuits';
import { buildFieldPath, getSerializationAttrFromContext, getFieldSlotIndex } from '../verifiable';
import { Merklizer, Path } from '@iden3/js-jsonld-merklization';
import { byteEncoder } from '../utils';
import { poseidon } from '@iden3/js-crypto';
/**
* converts verifiable RevocationStatus model to circuits structure
*
* @param {RevocationStatus} - credential.status of the verifiable credential
* @returns {ClaimNonRevStatus}
*/
export const toClaimNonRevStatus = (s) => {
if (!s) {
const hash = poseidon.hash(new Array(3).fill(0n));
return {
proof: new Proof(),
treeState: {
state: Hash.fromBigInt(hash),
claimsRoot: ZERO_HASH,
revocationRoot: ZERO_HASH,
rootOfRoots: ZERO_HASH
}
};
}
return {
proof: s.mtp,
treeState: buildTreeState(s.issuer.state, s.issuer.claimsTreeRoot, s.issuer.revocationTreeRoot, s.issuer.rootOfRoots)
};
};
/**
* converts state info from smart contract to gist proof
*
* @param {StateProof} smtProof - state proof from smart contract
* @returns {GISTProof}
*/
export const toGISTProof = (smtProof) => {
let existence = false;
let nodeAux;
if (smtProof.existence) {
existence = true;
}
else {
if (smtProof.auxExistence) {
nodeAux = {
key: Hash.fromBigInt(smtProof.auxIndex),
value: Hash.fromBigInt(smtProof.auxValue)
};
}
}
const allSiblings = smtProof.siblings.map((s) => Hash.fromBigInt(s));
const proof = new Proof({ siblings: allSiblings, nodeAux: nodeAux, existence: existence });
const root = Hash.fromBigInt(smtProof.root);
return {
root,
proof
};
};
export const parseCredentialSubject = (credentialSubject) => {
// credentialSubject is empty
if (!credentialSubject) {
return [{ operator: QueryOperators.$noop, fieldName: '' }];
}
const queries = [];
const entries = Object.entries(credentialSubject);
if (!entries.length) {
throw new Error(`query must have at least 1 predicate`);
}
for (const [fieldName, fieldReq] of entries) {
const fieldReqEntries = Object.entries(fieldReq);
const isSelectiveDisclosure = fieldReqEntries.length === 0;
if (isSelectiveDisclosure) {
queries.push({ operator: QueryOperators.$sd, fieldName: fieldName });
continue;
}
for (const [operatorName, operatorValue] of fieldReqEntries) {
if (!QueryOperators[operatorName]) {
throw new Error(`operator is not supported by lib`);
}
const operator = QueryOperators[operatorName];
queries.push({ operator, fieldName, operatorValue });
}
}
return queries;
};
export const parseQueryMetadata = async (propertyQuery, ldContextJSON, credentialType, options) => {
const query = {
...propertyQuery,
slotIndex: 0,
merklizedSchema: false,
datatype: '',
claimPathKey: BigInt(0),
values: [],
path: new Path()
};
if (!propertyQuery.fieldName && propertyQuery.operator !== Operators.NOOP) {
throw new Error('query must have a field name if operator is not $noop');
}
if (propertyQuery.fieldName) {
query.datatype = await Path.newTypeFromContext(ldContextJSON, `${credentialType}.${propertyQuery.fieldName}`, options);
}
const serAttr = await getSerializationAttrFromContext(JSON.parse(ldContextJSON), options, credentialType);
if (!serAttr) {
query.merklizedSchema = true;
}
// for merklized credentials slotIndex in query must be equal to zero
// and not a position of merklization root.
// it has no influence on check in the off-chain circuits, but it aligns with onchain verification standard
if (!query.merklizedSchema) {
query.slotIndex = await getFieldSlotIndex(propertyQuery.fieldName, credentialType, byteEncoder.encode(ldContextJSON));
}
else {
try {
const path = await buildFieldPath(ldContextJSON, credentialType, propertyQuery.fieldName, options);
query.claimPathKey = await path.mtEntry();
query.path = path;
}
catch (e) {
throw new Error(`field does not exist in the schema ${e.message}`);
}
}
if (propertyQuery.operatorValue !== undefined) {
if (!isValidOperation(query.datatype, propertyQuery.operator)) {
throw new Error(`operator ${propertyQuery.operator} is not supported for datatype ${query.datatype}`);
}
if ((propertyQuery.operator === Operators.NOOP || propertyQuery.operator === Operators.SD) &&
propertyQuery.operatorValue) {
throw new Error(`operator value should be undefined for ${propertyQuery.operator} operator`);
}
let values;
switch (propertyQuery.operator) {
case Operators.NOOP:
case Operators.SD:
values = [];
break;
case Operators.EXISTS:
values = transformExistsValue(propertyQuery.operatorValue);
break;
default:
values = await transformQueryValueToBigInts(propertyQuery.operatorValue, query.datatype);
}
query.values = values;
}
return query;
};
export const parseQueriesMetadata = async (credentialType, ldContextJSON, credentialSubject, options) => {
const queriesMetadata = parseCredentialSubject(credentialSubject);
return Promise.all(queriesMetadata.map((m) => parseQueryMetadata(m, ldContextJSON, credentialType, options)));
};
export const transformQueryValueToBigInts = async (value, ldType) => {
const values = [];
if (Array.isArray(value)) {
for (let index = 0; index < value.length; index++) {
values[index] = await Merklizer.hashValue(ldType, value[index]);
}
}
else {
values[0] = await Merklizer.hashValue(ldType, value);
}
return values;
};
const transformExistsValue = (value) => {
if (typeof value == 'boolean') {
return [BigInt(value)];
}
throw new Error('exists operator value must be true or false');
};