일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- express실행
- ws 라이브러리
- JWT 만들어보기
- 라우트 매개변수
- JWT 하드코딩
- mysql wsl
- JWT 로그인 기능 구현
- 라우터미들웨어 분리
- buffer.from
- FormData()
- 비동기파일업로드
- express session
- 세션으로 로그인 구현
- 아이디 중복체크기능
- 라우터 분리
- OAuth 카카오
- nodejs파일업로드
- useContext
- 라우터와 미들웨어
- 시퀄라이즈 기본설정
- node.js path
- javascript기초
- css기초
- next 매개변수
- express router
- Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
- useEffect clean up
- 블록 만들기
- cookie-parser 만들어보기
- express.static
- Today
- Total
즐코
스마트 컨트랙트로 초간단 투표 dApp 만들기 본문
지금까지 배운 솔리디티 개념을 바탕으로 매우 간단한 투표 dApp을 아래의 흐름대로 만들 수 있다.
이번에도 간단하게 truffle을 이용하여 스마트 컨트랙트를 작성하고 ganache 테스트 네트워크 상에 배포 및 컨트랙트 실행도 해보려고 한다. (물론 테스트로만)
크게 아래의 흐름으로 솔리디티 코드를 짤 것이다.
0. 상태변수 선언 : 후보자 리스트, 후보자별 기록된 득표수
1. 후보자 초기화 - 스마트 컨트랙트 배포 시 후보자들 등록하기
2. 후보자에 대한 투표 기능
3. 후보자에 대한 득표수 확인 기능
4. 테스트 코드 작성
0. 상태변수 선언
후보자 리스트 (candidate) 및 후보자별 기록된 득표수 (votesRcvd) 를 상태변수값으로 가지게 만든다.
두 변수 다 public 접근제한자로서 getter 함수가 자동으로 만들어지게끔 해준다.
1. 후보자 초기화
저번에도 공부했듯이 스마트 컨트랙트에 대한 인스턴스는 딱 한번 생성되는데 그건 바로 스마트 컨트랙트를 배포할 때이다.
따라서, 배포 시점에 정해진 후보자들을 인자값으로 넣기 위해 생성자 함수인 constructor() 를 사용하여 후보자리스트를 받아오자..
스마트 컨트랙트를 배포하는 시점에 후보자들을 등록해주는 것이다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
contract Voting {
string[] public candidateList;
mapping(string => uint8) public votesRcvd;
// 컨트랙트 배포 시점에 후보자군을 넣어준다
construct(string[] memory candidateName){
candidataList = candidateName;
}
}
migration 파일 작성 시 후보자군을 인자로 넣어준다.
// migration/2_deploy_Voting.js
const Voting = artifacts.require("Voting");
module.exports = function (deployer){
deployer.deploy(Voting, ["피카츄", "파이리", "꼬부기", "푸린"]);
}
2. 후보자에 대한 투표 기능
우선, 투표 시 무효표 방지를 위해, 투표할 때 받는 후보자명이 배포 시 등록했던 후보자 명단에 꼭 있어야 한다.
물론 투표 실행 함수에서 이를 걸러줘도 되지만 검증을 위한 목적으로만 함수를 따로 만들어주면 재사용성도 올라가고 하나의 함수가 하나의 기능만을 하기 때문에 더 좋을 듯 하다. 검증용 함수인 validCandidate는 굳이 public으로 할 필요가 없으므로 private으로 만들어준다.
function validCandidate(string memory candidate) private view returns (bool) {
for(uint i=0; i < candidateList.length; i++){
if(keccak256(abi.encodePacked(candidateList[i])) == keccak256(abi.encodePacked(candidate))){
return true;
}
}
return false;
}
function voteForCandidate(string memory candidate) public {
require(validCandidate(candidate), "ERROR! - Invalid candidate");
votesRcvd[candidate] += 1;
}
3. 후보자에 대한 득표수 확인 기능
여기서도 인자로 후보자 이름을 받으므로 이 후보자가 후보자 명단에 있는 후보인지 확인을 거치기 위해 validCandidate 함수를 사용해준다. 득표수는 간단히 상태변수 mapping으로 확인 가능하다.
function totalVotes(string memory candidate) public view returns (uint8) {
require(validCandidate(candidate), "ERROR! - Invalid candidate");
return votesRcvd[candidate];
}
4. 테스트 코드 작성
이제 테스트 코드를 작성해서 잘 돌아가는 컨트랙트인지 확인해보면 된다.
후보자 리스트를 가져올 때 deployed.candidateList.call(0), deployed.candidateList.call(1)... 으로 배열 인덱스 하나하나를 불러와서 list 라는 변수에 담아주었는데, 이는 이더리움 네트워크에서는 배열 전체를 한꺼번에 가져오는 게 불가능해서라고 한다. 따라서 배열을 가져오려면 상태변수 배열의 요소 하나 하나에 대한 요청 (call) 을 보내서 값을 가져오는 방식으로 전체를 가져오는 수밖에 없다.
// test/Voting.test.js
const Voting = artifacts.require("Voting");
// describe.only면 truffle test 시 해당 테스트들만 돌아간다!
describe.only("Voting", () => {
let deployed, list;
it("deployed", async () => {
deployed = await Voting.deployed();
});
it("candidateList", async () => {
// const candidate1 = await deployed.candidateList.call(0);
// const candidate2 = await deployed.candidateList.call(1);
// const candidate3 = await deployed.candidateList.call(2);
// const candidate4 = await deployed.candidateList.call(3);
// 각 1초씩이라고 치면 총 4초가 걸림 => Promise.all 을 사용하자!
const request = [
deployed.candidateList.call(0),
deployed.candidateList.call(1),
deployed.candidateList.call(2),
deployed.candidateList.call(3),
];
list = await Promise.all(request);
console.log("후보자 명단", list);
});
it("voteForCandidate", async () => {
// 투표하고
await Promise.call([
deployed.voteForCandidate(list[0]),
deployed.voteForCandidate(list[1]),
deployed.voteForCandidate(list[2]),
deployed.voteForCandidate(list[2]),
deployed.voteForCandidate(list[3]),
deployed.voteForCandidate(list[3]),
deployed.voteForCandidate(list[3]),
])
// 득표수 확인
for (const candidate of list) {
let count = await deployed.totalVotes.call(candidate);
console.log(`${candidate}:${count}`);
}
});
});
테스트 결과는 아래와 같다! 푸린 3표로 승.
'BlockChain' 카테고리의 다른 글
ERC20 interface / ERC20 (0) | 2022.07.22 |
---|---|
ETHER 전송을 위한 스마트 컨트랙트 작성 (payable) (0) | 2022.07.21 |
스마트 컨트랙트로 토큰 발행 및 전송해보기 (0) | 2022.07.19 |
Crypto Zombie / lesson2 (0) | 2022.07.19 |
Crypto Zombie / lesson1 - Overview (0) | 2022.07.18 |