Private Guest List Contract
This Compact contract implements a party with a private guest list. It offers demonstration of the following features:
- Hiding information on the public ledger
- Verifying hidden ledger information
- Access control to circuits
- Operations on a
Set - Introduction to witness functions
pragma language_version 0.22;
import CompactStandardLibrary;
export enum PartyState {
NOT_READY,
READY
}
export ledger organizers: Set<Bytes<32>>;// organizers are public
export ledger hashedPartyGoers: Set<Bytes<32>>;// participants are hashed until they arrive
export ledger checkedInParty: Set<Bytes<32>>;
export ledger partyState: PartyState;
export sealed ledger maxListSize: Uint<8>;
witness localStartParty(): PartyState;
witness localSk(): Bytes<32>;
constructor(){
const _sk = localSk();
const pubKey = publicKey(_sk);
organizers.insert(disclose(pubKey));
partyState = PartyState.NOT_READY;
maxListSize = 99;
}
export circuit addOrganizer(_organizerSk: Bytes<32>): [] {
const _sk = localSk();
const pubKey = publicKey(_sk);
assert(organizers.member(disclose(pubKey)), "You are not an organizer");
const newOrgPubKey = publicKey(_organizerSk);
assert(!organizers.member(disclose(newOrgPubKey)), "You are already in the organizer list");
assert(partyState == PartyState.NOT_READY, "The party has already started");
organizers.insert(disclose(newOrgPubKey));
}
export circuit addParticipant(_participantPk: Bytes<32>): [] {
// only organizers can add party goers
const _sk = localSk();
const pubKey = publicKey(_sk);
assert(organizers.member(disclose(pubKey)), "You are not an organizer");
assert(partyState == PartyState.NOT_READY, "The party has already started");
assert(hashedPartyGoers.size() < maxListSize, "The list is full");
const participant = commitWithSk(_participantPk, _sk);
assert(!hashedPartyGoers.member(disclose(participant)), "You are already in the list");
hashedPartyGoers.insert(disclose(participant));
if (hashedPartyGoers.size() == maxListSize) {
const localPartyState = localStartParty();
// don't trust, verify
assert(localPartyState == PartyState.READY, "Please start the party, the list is full");
partyState = PartyState.READY;
}
}
export circuit checkIn(participantPk: Bytes<32>): [] {
const _sk = localSk();
const pubKey = publicKey(_sk);
assert(organizers.member(disclose(pubKey)), "You are not an organizer");
assert(partyState == PartyState.READY, "The party has not started yet");
assert(checkedInParty.size() < hashedPartyGoers.size(), "All guests have already checked in");
assert(hashedPartyGoers.member(commitWithSk(participantPk, _sk)), "You are not on the list");
checkedInParty.insert(disclose(participantPk));
}
export circuit chainStartParty(): [] {
const _sk = localSk();
const pubKey = publicKey(_sk);
assert(organizers.member(disclose(pubKey)), "Only organizers can start the party");
assert(partyState == PartyState.NOT_READY, "The party has already started");
const localPartyState = disclose(localStartParty());
assert(localPartyState == PartyState.READY, "Please start the party locally");
partyState = localPartyState;
}
circuit commitWithSk(_participantPk: Bytes<32>, _sk: Bytes<32>): Bytes<32> {
const hash = persistentHash<Vector<2, Bytes<32>>>([_participantPk, _sk]);
return disclose(hash);
}
// hash a publicKey specific to this DApp so that users cannot be tracked
export circuit publicKey(_sk: Bytes<32>): Bytes<32> {
return persistentHash<Vector<2, Bytes<32>>>([pad(32, "guest-list:pk:"), _sk]);
}