UNPKG

mina-attestations

Version:
194 lines 7.86 kB
import { VerificationKey, DynamicProof, FeatureFlags, Poseidon, verify, ZkProgram, PublicKey, Undefined, } from 'o1js'; import { ProvableType } from "./o1js-missing.js"; import { inferNestedProvable, NestedProvable, } from "./nested.js"; import { prefixes } from "./constants.js"; import { hashCredential, withOwner, } from "./credential.js"; import { assert } from "./util.js"; import { deserializeProvableType, replaceNull, replaceUndefined, serializeProvableType, } from "./serialize-provable.js"; export { Imported, ImportedWitnessSpec }; const Imported = { create: createImported, fromProgram: importedFromProgram, fromMethod: importedFromMethod, publicInputType, Generic: { witnessType(witnessSpec) { assert(witnessSpec?.type === 'imported'); let { publicInputType, publicOutputType, maxProofsVerified, featureFlags, } = witnessSpec; class Proof extends DynamicProof { static publicInputType = ProvableType.get(publicInputType); static publicOutputType = ProvableType.get(publicOutputType); static maxProofsVerified = maxProofsVerified; static featureFlags = featureFlags; } return { type: ProvableType.constant('imported'), vk: VerificationKey, proof: Proof, }; }, // verify the proof, check that its public output is exactly the credential verify({ vk, proof }, credHash) { proof.verify(vk); hashCredential(proof.publicOutput).assertEquals(credHash, 'Invalid proof output'); }, async validate({ vk, proof }, credHash) { let ok = await verify(proof, vk); assert(ok, 'Invalid proof'); hashCredential(proof.publicOutput).assertEquals(credHash, 'Invalid proof output'); }, matchesSpec(witness) { // TODO should check proof type return witness.type === 'imported'; }, }, }; function createImported(spec) { return { credentialType: 'imported', data: NestedProvable.get(inferNestedProvable(spec.data)), witness: spec.witness, witnessType: Imported.Generic.witnessType, verify: Imported.Generic.verify, validate: Imported.Generic.validate, matchesSpec: Imported.Generic.matchesSpec, // issuer == hash of vk and public input issuer({ vk, proof }) { let credIdent = Poseidon.hash(ProvableType.get(spec.witness.publicInputType).toFields(proof.publicInput)); return Poseidon.hashWithPrefix(prefixes.issuerImported, [ vk.hash, credIdent, ]); }, }; } function witnessSpecToJSON(spec) { return { type: spec.type, publicInputType: serializeProvableType(spec.publicInputType), publicOutputType: serializeProvableType(spec.publicOutputType), maxProofsVerified: spec.maxProofsVerified, featureFlags: replaceUndefined(spec.featureFlags), }; } function witnessSpecFromJSON(json) { return { type: json.type, publicInputType: deserializeProvableType(json.publicInputType), publicOutputType: deserializeProvableType(json.publicOutputType), maxProofsVerified: json.maxProofsVerified, featureFlags: replaceNull(json.featureFlags), }; } const ImportedWitnessSpec = { toJSON: witnessSpecToJSON, fromJSON: witnessSpecFromJSON, }; function publicInputType(credentialSpec) { assert(credentialSpec.witness?.type === 'imported'); return credentialSpec.witness.publicInputType; } async function importedFromProgram(program) { const featureFlags = await FeatureFlags.fromZkProgram(program); const maxProofsVerified = await program.maxProofsVerified(); class InputProof extends DynamicProof { static publicInputType = ProvableType.get(program.publicInputType); static publicOutputType = ProvableType.get(program.publicOutputType); static maxProofsVerified = maxProofsVerified; static featureFlags = featureFlags; } let data = ProvableType.synthesize(program.publicOutputType).data; let dataType = NestedProvable.get(NestedProvable.fromValue(data)); let self = { spec: createImported({ data: dataType, witness: { type: 'imported', publicInputType: program.publicInputType, publicOutputType: program.publicOutputType, maxProofsVerified, featureFlags, }, }), program, isCompiled: false, verificationKey: undefined, async create(...inputs) { let vk = await self.compile(); let { proof } = await program.run(...inputs); return self.fromProof(proof, vk); }, async fromProof(proof, vk) { let dynProof = InputProof.fromProof(proof); return { version: 'v0', metadata: undefined, credential: proof.publicOutput, witness: { type: 'imported', vk, proof: dynProof }, }; }, async compile(options) { if (self.isCompiled) return self.verificationKey; let result = await program.compile(options); self.isCompiled = true; self.verificationKey = result.verificationKey; return self.verificationKey; }, async dummy({ owner, data, }) { let input = ProvableType.synthesize(program.publicInputType); let vk = await self.compile(); let credential = { owner, data: dataType.fromValue(data) }; let dummyProof = await InputProof.dummy(input, credential, maxProofsVerified); return { version: 'v0', metadata: undefined, credential, witness: { type: 'imported', vk, proof: dummyProof }, }; }, }; return self; } // TODO type returned from this should be annotated async function importedFromMethod(spec, method) { let publicInput = spec.publicInput === undefined ? undefined : NestedProvable.get(spec.publicInput); let privateInput = spec.privateInput === undefined ? Undefined : NestedProvable.get(spec.privateInput); let publicOutput = NestedProvable.get(withOwner(spec.data)); async function wrappedMethod(pub, priv, owner) { let data = await method({ publicInput: pub, privateInput: priv, owner }); return { publicOutput: { owner, data } }; } let program = ZkProgram({ name: spec.name, publicInput, publicOutput, methods: { run: { privateInputs: [privateInput, PublicKey], method: publicInput === undefined ? (privateInput, owner) => wrappedMethod(undefined, privateInput, owner) : wrappedMethod, }, // ZkProgram's generics are too stupid }, }); let credentialSpec = await importedFromProgram(program); return Object.assign(credentialSpec, { async create(inputs) { let vk = await credentialSpec.compile(); let proof; if (publicInput === undefined) { ({ proof } = await program.run(privateInput.fromValue(inputs.privateInput), inputs.owner)); } else { ({ proof } = await program.run(publicInput.fromValue(inputs.publicInput), privateInput.fromValue(inputs.privateInput), inputs.owner)); } return credentialSpec.fromProof(proof, vk); }, }); } //# sourceMappingURL=credential-imported.js.map