Cute Bow Tie Hearts Blinking Pink Pointer

블록체인/스마트 컨트랙트

[스마트 컨트랙트] 투표 dApp 만들어 보기

청포도 에이드 2022. 7. 18. 16:24
728x90

트러플을 활용해서 투표 기능이 있는 간단한 dApp 코드를 간단하게 작성하고 jest로 테스트 해보겠다.

 

목차

- sol코드

- jest코드

 

 

mkdir truffle
cd truffle
truffle init

터미널 새로열고

npx ganache-cli

 

trffle-config.js

development 부분 주석해제

 

 

truffle/contracts/Voting.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract Voting {
    string[] public candidateList; //배열 정의
    mapping(string=>uint8) public votesReceived; // 키값에 string을 넣으면 uint8으로 value값이 나오는 배열 voteReceived 정의

    constructor(string[] memory candidatenames){ // 후보 이름 담긴 배열을 넣어주면
        candidateList = candidatenames; //candidateList라는 배열에 저장된다.
        
    }
}

Voting이라는 contracts 를 정의해준다.

 

아래에선,

 

사용할 함수를 정의해주겠다.

 

1. 투표를 할 함수

변수명에 사용자 이름을 넣어주면 votesReived라는 배열에 투표 받은 수만큼 count가 올라감

    function voteForcandidate(string memory candidate) public{
        // 조건 require()
        require(vaildCandidate(candidate), "Error !");
        votesReceived[candidate] += 1;

    }

 

2. 총 투표를 받은 수를 출력하는 함수

인자 값으로 후보 이름을 넣어주면 된다.

    function totalVotesFor(string memory candidate) public view returns(uint8){
        require(vaildCandidate(candidate), "Error !"); //검증되지 않은 후보자면 error를 띄운다.
        return votesReceived[candidate]; // 받은 표수 return
    }

 

3. 후보자가 유효한지 검증해주는 함수

    function vaildCandidate(string memory candidate) private view returns(bool){
        // 1.후보자 리스트 candidateList
        // 2. 내가 입력한 후보자군과 candidateList 안에 후보자가 일치하는 게 있니?

        // String 비교가 안됩니다.
        // keccak256 16진수 -> 32바이트
        // password
        // keccak256(abi.encodePacked((candidateList[i])));)
        for(uint i=0; i< candidateList.length; i++){
            if(keccak256(abi.encodePacked(candidateList[i])) == keccak256(abi.encodePacked(candidate))){
                return true;
            }
        }
        return false;
    }

keccak256 그냥 sha-256이랑 비슷하다고 생각하자!

 

 

전체 코드

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract Voting {
    string[] public candidateList;
    mapping(string=>uint8) public votesReceived;

    constructor(string[] memory candidatenames){
        candidateList = candidatenames;
        
    }
    function voteForcandidate(string memory candidate) public{
        // 조건 require()
        require(vaildCandidate(candidate), "Error !");
        votesReceived[candidate] += 1;

    }

    function totalVotesFor(string memory candidate) public view returns(uint8){
        require(vaildCandidate(candidate), "Error !");
        return votesReceived[candidate];
    }
    function vaildCandidate(string memory candidate) private view returns(bool){
        // 1.후보자 리스트 candidateList
        // 2. 내가 입력한 후보자군과 candidateList 안에 후보자가 일치하는 게 있니?

        // String 비교가 안됩니다.
        // keccak256 16진수 -> 32바이트
        // password
        // keccak256(abi.encodePacked((candidateList[i])));)
        for(uint i=0; i< candidateList.length; i++){
            if(keccak256(abi.encodePacked(candidateList[i])) == keccak256(abi.encodePacked(candidate))){
                return true;
            }
        }
        return false;
    }
}

 

테스트 코드를 작성해보겠다.

truffle/test/Voting.test.js

 

const Voting = artifacts.require('Voting'); // 파일이름

describe.only('Voting', () => {
    let deployed, data;
    it('deployed', async () => {
        deployed = await Voting.deployed();
        console.log(deployed); // 배포 내역 확인
    });

    it('candidateList', async () => {
        const arr = [
            deployed.candidateList.call(0),
            deployed.candidateList.call(1), 
            deployed.candidateList.call(2), 
            deployed.candidateList.call(3), 
        ];
        data = await Promise.all(arr); // Promise객체 안에 한 번에 때려넣고 한 번에 돌림.
        console.log(data);
    });

    it('voteForCandidate', async () => {
        await deployed.voteForcandidate(data[0]); // 0번에게 투표
        await deployed.voteForcandidate(data[2]); // 2번에게 투표
        await deployed.voteForcandidate(data[1]); // 1번에게 투표
        await deployed.voteForcandidate(data[3]); // 3번에게 투표

        for (const candidate of data) {
            let count = await deployed.totalVotesFor.call(candidate);
            console.log(`${candidate} : ${count}`);
        }
    });
});

 

그 전에 migration 파일을 작성해주어야한다.

 

truffle/migrations/3_deploy.Voting.js

 

const Voting = artifacts.require('Voting');

module.exports = function (deployer) {
    deployer.deploy(Voting, ['사과', '딸기', '포도', '배']);
};

cadidateList를 넣어주어야한다.

 

truffle test

 

truffle test같은 경우 자동으로 compile이 진행되기 때문에

migration을 안해줘도 된다.

 

테스트 결과

 

사과, 딸기, 포도, 배가 각자 1표씩 받았다!

 

주의 사항: forEach문은 비동기를 기다려주지 않으니, async함수를 forEach문 안에 넣으면 안된다.

for in이나 for of를 사용 할 것.

728x90