UNPKG

mina-attestations

Version:
98 lines 3.48 kB
import { PublicKey, Signature, Undefined, Field, Group, Poseidon, } from 'o1js'; import { inferNestedProvable, } from "./nested.js"; import { zip } from "./util.js"; import { hashDynamic, provableTypeMatches } from "./dynamic/dynamic-hash.js"; export { hashCredential, verifyCredentials, signCredentials, withOwner, Unsigned, unsafeMissingOwner, createUnsigned, credentialMatchesSpec, }; /** * Hash a credential. */ function hashCredential({ owner, data }) { let ownerHash = Poseidon.hash(owner.toFields()); let dataHash = hashDynamic(data); return Poseidon.hash([ownerHash, dataHash]); } function verifyCredentials({ context, ownerSignature, credentials, }) { // pack credentials in hashes let credHashes = credentials.map(({ credential }) => hashCredential(credential)); // verify each credential using its own verification method zip(credentials, credHashes).forEach(([{ spec, witness }, credHash]) => { spec.verify(witness, credHash); }); // create issuer hashes for each credential let issuers = credentials.map(({ spec, witness }) => spec.issuer(witness)); // assert that all credentials have the same owner, and determine that owner let owner; credentials.forEach(({ credential }) => { owner ??= credential.owner; // set to the first owner credential.owner.assertEquals(owner); }); // verify the owner signature if (owner !== undefined) { let ok = ownerSignature.verify(owner, [ context, ...zip(credHashes, issuers).flat(), ]); ok.assertTrue('Invalid owner signature'); } return { owner: owner ?? PublicKey.empty(), // use a (0,0) public key in case there are no credentials // credential-issuer pairs credentials: zip(credentials, issuers).map(([{ credential, witness }, issuer]) => ({ data: credential.data, witness, issuer, })), }; } function signCredentials(ownerKey, context, ...credentials) { let hashes = credentials.map(({ credential }) => hashCredential(credential)); let issuers = credentials.map(({ credentialType, witness }) => credentialType.issuer(witness)); return Signature.create(ownerKey, [context, ...zip(hashes, issuers).flat()]); } function credentialMatchesSpec(spec, credential) { // check version if (credential.version !== 'v0') return false; // credential-specific check if (!spec.matchesSpec(credential.witness)) return false; // check that the data type matches return provableTypeMatches(credential.credential.data, spec.data); } const UnsignedBase = { credentialType: 'unsigned', witness: undefined, witnessType() { return Undefined; }, // do nothing verify() { }, async validate() { }, // dummy issuer issuer() { return Field(0); }, // always matches matchesSpec() { return true; }, }; function Unsigned(data) { return { ...UnsignedBase, data: inferNestedProvable(data) }; } function unsafeMissingOwner() { return PublicKey.fromGroup(Group.generator); } function createUnsigned(data, metadata) { return { version: 'v0', metadata, credential: { owner: unsafeMissingOwner(), data }, witness: undefined, }; } // helpers to create derived types function withOwner(data) { return { owner: PublicKey, data }; } //# sourceMappingURL=credential.js.map