투표 DApp 코딩 실습, 메타마스크(MetaMask) 기초 설명

2021. 10. 12. 19:33Blockchain/Solidity

실습 순서

  1. 솔리디티 코드 작성
  2. 솔리디티 코드 컴파일
  3. 컴파일 결과 배포 // = 블럭에 내용 추가, 가나슈 재부팅 하면 여기서부터 다시 작업
  4. 컴파일 결과를 js코드로 내용 불러올 수 있는지 테스트
  5. html에서 javascript web3 활용하여 내용 불러오기

실습 시작

  1. vsCode터미널 2개 열기 // 1: Ganache, 2: 작업 공간
  2. 터미널 1 >> ganache-cli --host 0.0.0.0 // 가나슈 실행
  3. Voting.sol 파일 생성 // 솔리디티 코드 작성
    pragma solidity ^0.8.0;
    
    contract Voting {
        /*
            필요 기능
            1 후보자 초기화
            2 각 후보자에게 투표 가능
            3:후보자들의 각 투표 개수
        */
        
        // ['ingoo1', 'ingoo2', 'ingoo3']
        string[] public candidateList;
        mapping(string => uint) public voteReceived;
        constructor(string[] memory _candidateNames) public {
            candidateList = _candidateNames;
        }
    
        function voteForCandidate(string memory _candidate) public {
            voteReceived[_candidate] += 1;
        }
    
        // 후보자 명을 넣어주면 결과값이 투표 개수를
        function totalVotesFor(string memory _candidate) view public returns(uint){
            return voteReceived[_candidate];
        }
    
        // String 비교
        function validCandidate(string memory _candidate) view public returns(bool) {
            // string to byte
            // keccake256() 메서드 안에 byte 값 넣기
            for (uint i = 0; i < candidateList.length; i++) {
                if (keccak256(bytes(candidateList[i])) == keccak256(bytes(_candidate))) {
                    return true;
                }
            }
            return false;
        }
    
        /*
            arr = ['1', '2', '3'];
            searchText = '4'
    
            // 완전탐색 0(n)
            function check(txt) {
                for (let i = 0; i < arr.length; i++); {
                    if (arr[i] == txt) {
                        return true;
                    }
                    return false;
                }
            }
    
            console.log(check(serachText));
        */
    }​
  4. soljs --abi --bin [파일명] // 솔리디티 코드 컴파일
    * Voting_sol_Voting.abi, Voting_sol_Voting.bin 자동 생성 됨
  5. const Web3 = require('web3');
    const fs = require('fs');
    
    const ABI = JSON.parse(fs.readFileSync('./Voting_sol_Voting.abi').toString());
    const BYTECODE = fs.readFileSync('./Voting_sol_Voting.bin').toString();
    
    let web3 = new Web3('http://127.0.0.1:8545');
    
    // 블록 생성 할 때 솔리디티 컴파일한 abi파일을 인자값으로
    const deployContract = new web3.eth.Contract(ABI);
    
    deployContract.deploy({
        // 배포를 할 때 솔리디티 컴파일한 byte값을 넣음
        data: BYTECODE,
        arguments: [['ingoo1', 'ingoo2', 'ingoo3'].map(name => web3.utils.asciiToHex(name))], // { } 대괄호 없으면 return 생략 가능
    })
    .send({
        from: '0x7fE68fFb0395EFb89D3F9f163215eAe3C92f823d', // Available Accounts 첫번째, 가나슈 껐다키면 수정
        gas: '6721975',
    }, (error, result) => {
        console.log(error);
    })
    .then(newContract=>{
        console.log(newContract.options.address);
    })​
     여기까지 작업하고 node deploy로 실행 // newContract.options.address 값 가져오기
    const deployContract부터 주석 처리 후 뒤에 아래 내용 추가
    const contract = new web3.eth.Contract(ABI, '0x31dc15fd778b1d3bd66f9f621f8797dd1a0fe1d5') // Contract created, 가나슈 껐다키면 수정
    
    contract.methods.voteForCandidate('ingoo1').send({
        from: '0x71a1ef22df159fD4B9147d6B21eC04cD0511A583', // Available Accounts 중 0번 제외 아무거나, 가나슈 껐다키면 수정
    })
    
    contract.methods.totalVotesFor('ingoo1').call().then(data => {
        console.log(data);
    })​
     여기까지 작업하고 node deploy 재실행
    실행 할 때마다 블록이 추가되어 숫자가 하나씩 쌓이는게 확인 됨 // 0, 1, 2.....
    가나쉬에서는 GAS가 계속 소모되고 있음 (아래)
    * 가나슈를 재부팅 하면 쌓였던 블록들이나 배포했던 내용들이 다 사라지고, 컴파일도 다시 해야함
    * 코드도 수정이 필요하며 해당 내용은 아래 index.js 파일 내 주석으로 달아 놓음

  6. index.html 파일 생성 후 web3 라이브러리 가져오고, index.js 연결
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>투표 DApp</title>
    </head>
    <body>
        <ul>
            <li>
                <span id="candidate1"></span>
                <span id="candidateCount1"></span>
            </li>
            <li>
                <span id="candidate2"></span>
                <span id="candidateCount2"></span>
            </li>
            <li>
                <span id="candidate3"></span>
                <span id="candidateCount3"></span>
            </li>
        </ul>
        <input type="text" id="candidateName" />
        <button id="btn">투표</button>
    </body>
    <!--
        https://github.com/ChainSafe/web3.js
        web3의 라이브러리를 가져옴
    -->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <script type="text/javascript" src="./index.js"></script>
    </html>​
  7. index.js 파일 생성
    /* 가나쉬 연결 */
    
    const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
    
    const ABI = JSON.parse(`[{"inputs":[{"internalType":"string[]","name":"_candidateNames","type":"string[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"totalVotesFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"validCandidate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"voteForCandidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"voteReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`);  // .abi 파일 내용 복붙
    const deployAddress = `0x31dc15fd778b1d3bd66f9f621f8797dd1a0fe1d5`; // Contract created, 가나슈 껐다키면 수정
    
    let VotingContract = new web3.eth.Contract(ABI, deployAddress);
    let candidates = { // 테스트를 위해 하드코딩
        "ingoo1": "candidate1",
        "ingoo2": "candidate2",
        "ingoo3": "candidate3"
    };
    
    window.addEventListener('DOMContentLoaded', init);
    
    async function init() {
        let candidateNames = Object.keys(candidates); // ['ingoo1','ingoo2','ingoo3']
    
        for (let i = 0; i < candidateNames.length; i++) {
            let name = candidateNames[i];//ingoo1
            
            const nameElement = document.querySelector(`#${candidates[name]}`); // #candidate1
            nameElement.innerHTML = name;
    
            const countElement = document.querySelector(`#candidateCount${i + 1}`);
            countElement.innerHTML = await VotingContract.methods.totalVotesFor(`ingoo${i + 1}`).call();
        }
        // await VotingContract.methods.vodeForCandiodate('ingoo1').send({from:'0x71a1ef22df159fD4B9147d6B21eC04cD0511A583'});
        // VotingContract.methods.totalVotesFor('ingoo1').call().then(data=>{
        //     console.log(data);
        // })
    }
    
    const btn = document.querySelector('#btn');
    btn.addEventListener('click', btnEvent);
    
    async function btnEvent() {
        let candidateName = document.querySelector(`#candidateName`).value;
        // console.log(candidateName);
    
        await VotingContract.methods.voteForCandidate(candidateName).send({ from: '0x71a1ef22df159fD4B9147d6B21eC04cD0511A583' }) // Available Accounts 중 0번 제외 아무거나, 가나슈 껐다키면 수정
    
        let candidateCount = await VotingContract.methods.totalVotesFor(candidateName).call();
        let number = candidateName.charAt(candidateName.length - 1)
        let countElement = document.querySelector(`#candidateCount${number}`);
        countElement.innerHTML = candidateCount;
    }
  8. index.html 파일 라이브 서버로 열기
  9. input box에 ingoo3 입력 후 투표 버튼 누르면 트랜잭션이 발생하여 터미널도 갱신되며, html 페이지에서 오른쪽 숫자도 올라가는걸 확인 할 수 있음

메타마스크(MetaMask)

  • 메타마스크의 특징
    * 핫 월렛
    * 비수탁형 지갑

 

  • 메타마스크의 역할
    * 암호화폐의 지갑으로 코인마다 지갑이 다르기때문에 여러개의 주소를 보관해줌
    * 보관한 주소를 조회할 수도 있으며, 다른 주소로 코인을 보낼 수도 있음

 

  • 핫 월렛(HotWallet)  : 온라인 지갑
  • 콜드 월렛(ColdWallet) : 오프라인 지갑

 

  • 수탁형(trustee) 지갑 : 거래소에 저장 된 지갑, 제 3자에 의해서 보관 됨
  • 비수탁형(non-trustee) 지갑 : 사용자가 직접 관리하는 지갑

'Blockchain > Solidity' 카테고리의 다른 글

스마트 컨트랙트(Smart Contract)  (0) 2021.10.11
솔리디티(Solidity)  (0) 2021.10.08