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