# zPass invalidate hiding program

```leo
// 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zpass.docs.aleo.org/zpass-programs/zpass-invalidate-hiding-program.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
