mina-attestations
Version:
Private Attestations on Mina
194 lines • 7.86 kB
JavaScript
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