zPass merkle tree size 8 program
// The 'zpass_merkle_8' program.
program zpass_merkle_8.aleo {
// Example admin address that has authority to add or remove issuer
const ADMIN: address = aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px;
// The ZPass record
record ZPass {
owner: address,
issuer: address,
root: field,
}
// Stores approved issuers
mapping is_issuer: address => bool;
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);
}
function get_merkle_tree(leaves_hashes: [field; 8]) -> (field, [field; 2], [field; 4]) {
let third_level_nodes: [field; 4] = [
Poseidon2::hash_to_field(leaves_hashes[0u8] + leaves_hashes[1u8]),
Poseidon2::hash_to_field(leaves_hashes[2u8] + leaves_hashes[3u8]),
Poseidon2::hash_to_field(leaves_hashes[4u8] + leaves_hashes[5u8]),
Poseidon2::hash_to_field(leaves_hashes[6u8] + leaves_hashes[7u8]),
];
let second_level_nodes: [field; 2] = [
Poseidon2::hash_to_field(third_level_nodes[0u8] + third_level_nodes[1u8]),
Poseidon2::hash_to_field(third_level_nodes[2u8] + third_level_nodes[3u8]),
];
let root_hash: field = Poseidon2::hash_to_field(second_level_nodes[0u8] + second_level_nodes[1u8]);
return (root_hash, second_level_nodes, third_level_nodes);
}
// External function to get the merkle tree
transition get_merkle(leaves_hashed: [field; 8]) -> (field, [field; 2], [field; 4]) {
return get_merkle_tree(leaves_hashed);
}
async transition issue(
private sig: signature,
private leaves_hashes: [field; 8],
public issuer: address,
) -> (ZPass, Future) {
// Verify the first leaf is the issuer
assert_eq(leaves_hashes[0u8], Poseidon2::hash_to_field(issuer));
// Get the merkle tree
let merkle_tree: (field, [field; 2], [field; 4]) = get_merkle_tree(leaves_hashes);
// Verify signature against root
assert_eq(signature::verify(sig, issuer, merkle_tree.0), true);
// Return the ZPass record and pass the commitment to store in the mapping
return (ZPass {
owner: self.signer,
issuer: issuer,
root: merkle_tree.0,
}, issue_finalize(issuer));
}
async function issue_finalize(
public issuer: address,
) {
// Ensure the issuer is approved
assert_eq(is_issuer.get(issuer), true);
}
// msg and r are used to construct a public commitment for ownership verification
// Any message works as long the prover is able to open the commitment
transition verify(
msg: field,
r: scalar,
zpass: ZPass,
leaf_hash: field,
merkle_proof: [field; 3]
) -> (
public field,
bool,
ZPass
) {
let element: field = leaf_hash;
for i: u8 in 0u8..3u8 {
// Order does not matter because of the commutative property of group addition
element = Poseidon2::hash_to_field(element + merkle_proof[i]);
}
assert_eq(element, zpass.root);
// Creates the commitment and return publicly
let commitment: field = BHP256::commit_to_field(msg, r);
// Return ZPass to avoid burning after validation
return (commitment, true, zpass);
}
}Overview
How It Works
Last updated