zPass invalidate hiding program
// The 'zpass_invalidate_hiding' program.
program zpass_invalidate_hiding.aleo {
// Example admin address that has authority to add or remove issuer
const ADMIN: address = aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px;
// The ZPass record
record ZPass {
owner: address,
issuer: group,
dob: group,
nationality: group,
expiry: group,
salt: scalar
}
struct InvalidateZPass {
issuer: group,
subject: address,
dob: group,
nationality: group,
expiry: group,
salt: scalar
}
// The private credential struct
struct PrivateCredential {
subject: address,
dob: u32,
nationality: field,
expiry: u32
}
// The public credential struct
// This is the public information that is shared publicly onchain
struct PublicCredential {
issuer: address,
salt: scalar
}
// The full credentials struct to be hashed and verified against the signature
struct FullCredentials {
issuer: address,
subject: address,
dob: u32,
nationality: field,
expiry: u32,
salt: scalar
}
// Stores approved issuers
mapping is_issuer: address => bool;
// Store the commitment of the user address to prevent double issuance
mapping issued: group => bool;
// Store the commitment of the credentials to invalidate the ZPass
mapping invalidated: group => bool;
// The count of issued ZPasses
// Using key of 0field as global key
mapping issuance_count: field => u128;
async transition add_issuer(
public issuer: address
) -> Future {
assert_eq(self.caller, ADMIN);
return add_issuer_finalize(issuer);
}
async function add_issuer_finalize(
issuer: address
) {
is_issuer.set(issuer, true);
}
async transition remove_issuer(
public issuer: address
) -> Future {
assert_eq(self.caller, ADMIN);
return remove_issuer_finalize(issuer);
}
async function remove_issuer_finalize(
issuer: address
) {
is_issuer.set(issuer, false);
}
async transition issue(
private sig: signature,
private pri: PrivateCredential,
public pub: PublicCredential,
) -> (ZPass, Future) {
// Construct the full credentials struct to be hashed and verified against the signature
let credentials: FullCredentials = FullCredentials {
issuer: pub.issuer,
subject: self.signer, // The signer must be the subject of the ZPass
dob: pri.dob,
nationality: pri.nationality,
expiry: pri.expiry,
salt: pub.salt,
};
// Verify signature
assert_eq(signature::verify(sig, pub.issuer, Poseidon2::hash_to_field(credentials)), true);
// Compute commitment to prevent double issuance
let commit: group = BHP256::commit_to_group(self.signer, pub.salt);
// Return the ZPass record and pass the commitment to store in the mapping
return (ZPass {
owner: self.signer,
issuer: BHP256::commit_to_group(pub.issuer, pub.salt),
dob: BHP256::commit_to_group(pri.dob, pub.salt),
nationality: BHP256::commit_to_group(pri.nationality, pub.salt),
expiry: BHP256::commit_to_group(pri.expiry, pub.salt),
salt: pub.salt
}, issue_finalize(pub.issuer, commit));
}
async function issue_finalize(
public issuer: address,
public commit: group
) {
// Ensure the issuer is approved
assert_eq(is_issuer.get(issuer), true);
// Ensure the commitment is not already issued
assert_eq(issued.get_or_use(commit, false), false);
// Store the commitment in the mapping
issued.set(commit, true);
// Increment the issuance count
issuance_count.set(0field, issuance_count.get_or_use(0field, 0u128) + 1u128);
}
// Must know the details of the ZPass to invalidate it
// subject in FullCredentials is owner of the ZPass to invalidate
async transition invalidate(
credentials: InvalidateZPass
) -> Future {
let commit: group = BHP256::commit_to_group(credentials, credentials.salt);
return invalidate_finalize(commit);
}
async function invalidate_finalize(
public commit: group
) {
// Store the commitment in the mapping
invalidated.set(commit, true);
}
// Helper function to check validity of ZPass
async transition is_invalid(
credentials: InvalidateZPass
) -> Future {
let commit: group = BHP256::commit_to_group(credentials, credentials.salt);
return is_invalid_finalize(commit);
}
async function is_invalid_finalize(
public commit: group
) {
assert_eq(invalidated.get_or_use(commit, false), false);
}
// Sample transition to check if the dob is more than certain age
transition more_than_18(
dob: u32,
dob_check: u32,
zpass: ZPass
) -> ZPass {
// Ensure knowledge of the dob commitment
assert_eq(zpass.dob, BHP256::commit_to_group(dob, zpass.salt));
// Ensure the dob is more than certain age
assert_eq(dob > dob_check, true);
// Return ZPass to avoid burning after validation
return zpass;
}
}
This example demonstrates the integration of both the zPass invalidation and data-hiding functionalities, showcasing how these features can work together seamlessly within a single program.
Last updated