Skip to main content

선거 컨트랙트

이 Compact 컨트랙트는 두 후보자와 등록된 유권자 사이의 선거를 구현합니다. 다음 기능을 시연합니다:

  • 비공개 상태 정보에 대한 커밋
  • SetMap 연산
  • 이 DApp 외부에서 사용자를 추적할 수 없도록 하는 DApp 전용 공개 키 함수
pragma language_version 0.22;
import CompactStandardLibrary;

export enum VotingState {
CLOSED,
OPEN
}

export enum VoteChoice {
BAD,
WORSE,
TIE
}

// sealed ledger 값은 생성자 실행 후 변경할 수 없습니다
export sealed ledger organizer: ZswapCoinPublicKey;
export sealed ledger candidate0: Opaque<"string">;
export sealed ledger candidate1: Opaque<"string">;
export ledger votingState: VotingState;
export ledger hashedVoteMap: Map<Bytes<32>, Bytes<32>>;
export ledger registeredVoters: Set<Bytes<32>>;
export ledger totalVoteCount: Counter;
export ledger candidate0VoteCounter: Counter;
export ledger candidate1VoteCounter: Counter;
export ledger winner: VoteChoice;

witness localGetVote(): VoteChoice;
witness localSk(): Bytes<32>;

constructor(candidateA: Opaque<"string">, candidateB: Opaque<"string">) {
organizer = ownPublicKey();
candidate0 = disclose(candidateA);
candidate1 = disclose(candidateB);
votingState = VotingState.CLOSED;
}

// 시간 기반으로 이용 가능해야 합니다
export circuit registerToVote(): [] {
const _sk = localSk();
const dappPublicKey = getDappPublicKey(_sk);
assert(!registeredVoters.member(disclose(dappPublicKey)), "You are already registered to vote");
assert(votingState == VotingState.CLOSED, "You can only register before voting starts");

registeredVoters.insert(disclose(dappPublicKey));
}

export circuit openVoting(): [] {
assert(organizer == ownPublicKey(), "You are not the organizer");
assert(votingState == VotingState.CLOSED, "Voting has already been openend");
assert(!registeredVoters.isEmpty(), "There are no registered voters");

votingState = VotingState.OPEN;
}

export circuit commitVote(): [] {
assert(votingState == VotingState.OPEN, "Voting has not opened yet");
const _sk = localSk();
const dappPublicKey = getDappPublicKey(_sk);
assert(registeredVoters.member(dappPublicKey), "You are not registered to vote.");
assert(!hashedVoteMap.member(dappPublicKey), "Attempt to double vote");
const _currentVote = localGetVote();
assert(_currentVote == VoteChoice.BAD || _currentVote == VoteChoice.WORSE, "Please provide a valid vote");


const hash = commitWithSk(_currentVote as Field as Bytes<32>, _sk);
hashedVoteMap.insert(dappPublicKey, hash);
totalVoteCount.increment(1);
}

// 원래 투표를 privateState에서 확인합니다. 변경되었다면 오류가 발생합니다
export circuit revealVote(): [] {
assert(votingState == VotingState.CLOSED, "Voting is still open");

const _sk = localSk();
const dappPublicKey = getDappPublicKey(_sk);
// 이미 투표했는지 확인합니다
assert(hashedVoteMap.member(dappPublicKey), "You have not voted yet");
assert(registeredVoters.member(dappPublicKey), "You are not a registered voter");

const vote = localGetVote();
assert(vote == VoteChoice.BAD || vote == VoteChoice.WORSE, "Please supply a valid vote");

const hashedVote = commitWithSk(vote as Field as Bytes<32>, _sk);
assert(hashedVoteMap.lookup(dappPublicKey) == hashedVote, "Attempt to change the vote!");

if(disclose(vote) == VoteChoice.BAD){
candidate0VoteCounter.increment(1);
} else if (disclose(vote) == VoteChoice.WORSE) {
candidate1VoteCounter.increment(1);
}
}

// 시간 기반이어야 합니다
export circuit closeVoting(): [] {
assert(organizer == ownPublicKey(), "You are not the organizer");
assert(!hashedVoteMap.isEmpty(), "You can't close the voting with no votes");
votingState = VotingState.CLOSED;
}

export circuit checkWinner(): [] {
assert(organizer == ownPublicKey(), "You are not an organizer");
assert(votingState == VotingState.CLOSED, "Voting is still open");

// 승자를 확인합니다
if(candidate0VoteCounter > candidate1VoteCounter) {
winner = VoteChoice.BAD;
} else if (candidate0VoteCounter < candidate1VoteCounter) {
winner = VoteChoice.WORSE;
} else if (candidate0VoteCounter == candidate1VoteCounter) {
winner = VoteChoice.TIE;
}
}

// _sk로 해시 커밋하면 단순한 1 또는 2를 무차별 대입(brute force)하는 것이 불가능해집니다
circuit commitWithSk(_vote: Bytes<32>, _sk: Bytes<32>) : Bytes<32> {
return disclose(persistentHash<Vector<2, Bytes<32>>>([_vote, _sk]));
}

// 이 DApp에서만 추적 가능한 랜덤 "공개 키"를 해시합니다
export circuit getDappPublicKey(_sk: Bytes<32>): Bytes<32> {
return disclose(persistentHash<Vector<2, Bytes<32>>>([pad(32, "election:pk:"), _sk]));
}