WEEK 6

스마트 계약 심화 - 실습 가이드

대전대학교 대학원 | 블록체인 기술 특론 | 연삼흠 교수 | WIA-FIN-007

실습 진행률

0 / 22 완료
1

투표 컨트랙트 코딩

Remix IDE에서 투표(Voting) 스마트 계약을 작성합니다.

// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract Voting { // 후보자 구조체 struct Candidate { uint256 id; string name; uint256 voteCount; } // 투표 상태 enum State { Created, Voting, Ended } address public admin; State public state; mapping(uint256 => Candidate) public candidates; mapping(address => bool) public registeredVoters; mapping(address => bool) public hasVoted; uint256 public candidateCount; uint256 public totalVotes; // 이벤트 (WIA-FIN-007 감사 로그) event CandidateAdded(uint256 indexed id, string name); event VoterRegistered(address indexed voter); event VoteCast(address indexed voter, uint256 indexed candidateId); event StateChanged(State newState); // 접근 제어 modifier modifier onlyAdmin() { require(msg.sender == admin, "Admin only"); _; } modifier inState(State _state) { require(state == _state, "Invalid state"); _; } constructor() { admin = msg.sender; state = State.Created; } // 후보자 추가 (Created 상태에서만) function addCandidate(string memory _name) public onlyAdmin inState(State.Created) { candidateCount++; candidates[candidateCount] = Candidate(candidateCount, _name, 0); emit CandidateAdded(candidateCount, _name); } // 유권자 등록 function registerVoter(address _voter) public onlyAdmin { require(!registeredVoters[_voter], "Already registered"); registeredVoters[_voter] = true; emit VoterRegistered(_voter); } // 투표 시작 function startVoting() public onlyAdmin inState(State.Created) { require(candidateCount > 0, "No candidates"); state = State.Voting; emit StateChanged(State.Voting); } // 투표하기 function vote(uint256 _candidateId) public inState(State.Voting) { require(registeredVoters[msg.sender], "Not registered voter"); require(!hasVoted[msg.sender], "Already voted"); require(_candidateId > 0 && _candidateId <= candidateCount, "Invalid candidate"); hasVoted[msg.sender] = true; candidates[_candidateId].voteCount++; totalVotes++; emit VoteCast(msg.sender, _candidateId); } // 투표 종료 function endVoting() public onlyAdmin inState(State.Voting) { state = State.Ended; emit StateChanged(State.Ended); } // 당선자 조회 function getWinner() public view inState(State.Ended) returns (string memory winnerName, uint256 winnerVotes) { uint256 maxVotes = 0; uint256 winnerId = 0; for (uint256 i = 1; i <= candidateCount; i++) { if (candidates[i].voteCount > maxVotes) { maxVotes = candidates[i].voteCount; winnerId = i; } } return (candidates[winnerId].name, maxVotes); } }

코드 핵심 포인트

  • enum State: 계약의 생애주기를 3단계로 관리
  • modifier inState: 특정 상태에서만 함수 실행 허용
  • 모든 주요 동작에 event 발생 (WIA-FIN-007 준수)
  • 다중 require로 입력값 검증
2

컴파일 및 배포

주의

배포 후 Account 드롭다운에서 여러 계정을 사용할 예정입니다. 첫 번째 계정이 admin이 됩니다. 나머지 계정은 유권자로 사용합니다.

3

후보자 및 유권자 등록

4

투표 실행

이벤트 로그 확인

콘솔 하단에서 각 트랜잭션의 로그를 펼쳐보세요. VoteCast 이벤트에 voter 주소와 candidateId가 기록되어 있습니다. 이것이 WIA-FIN-007 감사 추적의 기본입니다.

5

결과 확인 및 프론트엔드 연결

프론트엔드 연결 안내

Web3.js 또는 ethers.js를 사용하여 이 계약의 ABI와 배포 주소로 프론트엔드를 연결할 수 있습니다. 심화 과제로 간단한 HTML 프론트엔드를 만들어 보세요.

WIA-FIN-007 시뮬레이터에서 검증하기