Skip to main content

Private Guest List Contract

For the complete documentation index, see llms.txt

이 Compact 컨트랙트는 비공개 게스트 리스트가 있는 파티를 구현합니다. 다음 기능을 시연합니다:

  • Set 연산
  • 네이티브 토큰(NIGHT) 연산
  • Midnight DApp의 프라이버시 경계
pragma language_version 0.23;
import CompactStandardLibrary;

export enum PartyState {
NOT_STARTED,
READY,
STARTED,
DOORS_CLOSED,
FEES_CLAIMED
}

export sealed ledger organizer: Bytes<32>;
export sealed ledger maxListSize: Uint<16>;
export sealed ledger entryFee: Uint<16>;
export ledger partyState: PartyState;
export ledger hashedPartyGoers: Set<Bytes<32>>;
export ledger checkedInParty: Set<UserAddress>;

witness localSecret(): Bytes<32>;

constructor (partySize: Uint<16>, fee: Uint<16>) {
const _secret = localSecret();
const pubKey = getDappPublicKey(_secret);
organizer = disclose(pubKey);

assert(partySize > 0, "The party size must be greater than zero");
assert(fee > 0, "Fee must be greater than zero");

entryFee = disclose(fee);
maxListSize = disclose(partySize);
partyState = PartyState.NOT_STARTED;
}

// 파티 참가자가 호출합니다
export circuit rsvp(_address: UserAddress): [] {
const _secret = localSecret();
const pubKey = getDappPublicKey(_secret);
// 호출자 인증 확인
assert(pubKey != organizer, "Organizer cannot RSVP to the party");

// 상태 검증 확인
assert(partyState == PartyState.NOT_STARTED, "The party has already started");
assert(hashedPartyGoers.size() < maxListSize, "The list is full");

// 참가자 주소는 비공개로 유지됩니다
const commitHash = commitAddress(_secret, _address.bytes);
assert(!hashedPartyGoers.member(commitHash), "You are already on the list");
hashedPartyGoers.insert(commitHash);// persistentCommit이므로 disclose가 필요 없습니다

if (hashedPartyGoers.size() == maxListSize) {
// @TODO -- 향후 여기서 주최자에게 이벤트를 발행할 예정 (MIP-0002)
partyState = PartyState.READY;
}
}

// 파티 시작 (주최자)
export circuit startParty(): [] {
const _secret = localSecret();
const pubKey = getDappPublicKey(_secret);
assert(organizer == pubKey, "Only the organizer can start the party");
assert(partyState == PartyState.READY || partyState == PartyState.NOT_STARTED,
"The party is not in the correct state for this operation");

partyState = PartyState.STARTED;
}

export circuit closeEntry(): [] {
const _secret = localSecret();
const pubKey = getDappPublicKey(_secret);
assert(organizer == pubKey, "Only organizer can close the doors");
assert(partyState == PartyState.STARTED, "Party in wrong state");

partyState = PartyState.DOORS_CLOSED;
}

// 참가자가 호출하므로 호출자에게 결제를 요청할 수 있습니다
// 이 circuit이 실행되고 나면 참가자는 공개됩니다
export circuit checkIn(address: UserAddress): [] {
// 상태 검증 확인
assert(partyState == PartyState.STARTED, "The party has not been started. Call the party police");
assert(checkedInParty.size() < hashedPartyGoers.size(), "All guests have already checked in");

const _secret = localSecret();
const commitHash = commitAddress(_secret, address.bytes);

// 호출자 검증 확인
assert(hashedPartyGoers.member(commitHash), "You are not on the list");
assert(!checkedInParty.member(disclose(address)), "You have already checked in");

// unshielded 결제를 수신하며, 이 시점부터 참가자는 공개됩니다
receiveUnshielded(nativeToken(), entryFee as Uint<128>);
checkedInParty.insert(disclose(address));

if(checkedInParty.size() == maxListSize) {
partyState = PartyState.DOORS_CLOSED;
}
}

export circuit claimFees(address: UserAddress): [] {
const _secret = localSecret();
const pubKey = getDappPublicKey(_secret);
assert(organizer == pubKey, "You are not the organizer");

// 상태 검증 확인
assert(partyState == PartyState.DOORS_CLOSED, "The doors are not yet closed");
assert(checkedInParty.size() > 0, "No fees to claim");

// 컨트랙트의 NIGHT 토큰 잔액을 계산합니다
const totalCollected = checkedInParty.size() * entryFee;
assert(unshieldedBalanceGte(nativeToken(), totalCollected), "Contract balance wrong");

// 주최자에게 전송합니다
sendUnshielded(
nativeToken(),
disclose(totalCollected) as Uint<128>,
right<ContractAddress, UserAddress>(disclose(address))
);
partyState = PartyState.FEES_CLAIMED;
}

circuit commitAddress(_secret: Bytes<32>, _address: Bytes<32>): Bytes<32> {
return persistentCommit<Bytes<32>>(_address, _secret);
}

// 사용자가 추적될 수 없도록 이 DApp에 특화된 publicKey를 해시합니다
// _secret은 충분히 복잡한 값이어야 합니다
circuit getDappPublicKey(_secret: Bytes<32>): Bytes<32> {
return persistentHash<Vector<2, Bytes<32>>>([pad(32, "private-party:pk:"), _secret]);
}