BlockChain>스마트 컨트랙트 입문

YU YU·2021년 10월 11일
0

경일_BlockChain

목록 보기
17/24
post-thumbnail

스마트 컨트랙트 만들기
이더리움 RPC통신을 할 수 있는 데몬을 설치할 수 있고, 이걸 통해서 스마트 컨트랙트를 실행할 수 있다.

1.프로그램 용어 정리

1-1.가나슈(Ganache)

테스트 목적으로 pc에 설치해서 사용할 수 있는 간이 블록체인.
네트워크와의 연결이 필요 없이 로컬에서 작동시킬 수 잇어 계약을 손쉽게 배포 및 테스트 해 볼 수 있다. 100이더가 미리 탑재된 10개의 테스트 계정을 확보해준다.
어플리케이션이 블록체인에 끼치는 영향을 GUI에서 확인할 수 있고, 잔액, 계약 생성, 가스 사용등 세부 정보도 확인할 수 있다. 이더리움 류 알트코인의 데몬을 간단히 만들 수 있게 하는 프로그램이라고 보면 편하다. --위키백과--

1-2.Web3.js

1-2-2. web3.js의 역할


거래소 만들기 프로젝트에서 rpc통신을 할 때 거래소(브라우저) 데몬을 백 서버가 연결해 주었다.

web3.js를 사용한다면 이렇게 간단해진다.


중간에 서버의 중개를 거치지 않고 직접 데몬과 소통할 수 있도록 할 수 있다.

2. 환경세팅

2-1. 설치하기

가나슈와 web3.js는 node.js환경에서 세팅이 가능하다.
먼저 visualStudio Code를 열고 터미널에 다음과 같이 입력한다.

npm init
npm install -g truffle
npm install -g ganache-cli
npm install web3

다 깔았으면 다음과 같이 입력해서 버전을 확인해본다.

truffle version
ganache-cli --host 0.0.0.0
(에러가 뜨면 npx ganache-cli --host 0.0.0.0)

특정 .js파일에서 데몬에게 직접 요청 보내기

공개키와 비밀키가 나와있다.

Gas에 대한 정보들도 나와있다.

3. 역할들

3-1. Gas

GAS는 스마트 컨트랙트를 배포하고 실행할 때 사용되는 수수료이다.
연산이 많을수록 GAS비용이 높아진다.

  • GAS 가격(Gas Price)

    스마트 컨트랙트를 발생할 때 스마트 컨트랙트를 작성한 사람이 설정하는 가스 가격이다.

  • Gas Limit

    최대 수수료
    내가 낼 수 있는 최대 수수료가격

3-2. Ganache

가나슈는 데몬, 즉 서버이기에 RPC 통신을 통해서 특정 주소의 이더리움의 갯수를 구하는 등의 역할을 할 수 있다.

eth_getBalance를 통해서

0.0.0.0:8545 eth_getBalance

3-2-1. 통신 연결해보기

curl -X POST -d '' http://127.0.0.1:8545
을 입력하면 400 error가 뜬다.

나는 이러한 오류가 떴다. 이건 윈도우와 리눅스가 서로 포트를 공유하지 못해서 생긴 오류같다.

3-2-2. eth_accounts

100개의 ETH가 들어있는 계좌번호를 반환하는 API이다 .

나는 오류가 떴다.
그래서 다음과 같이 임시방편으로 js파일을 만들어서 이렇게 실행시키고 postman으로 실행을 시켰다. ganache도 다시 폴더 안에 깔았다.

  • server.js
//server.js
var ganache = require("ganache-cli");
var server = ganache.server();
server.listen(8546, function(err, blockchain) {
    console.log(`listen`)
});

다음과 같이 계좌들이 나온다. 이 계좌에는 100eth씩 저장되어 있다.

3-2-3. eth_getBalance [주소값]

지갑주소안에 이더(ETH)가 얼마나 들어있는지 반환하는 API이다.

curl -X POST -d '{"jsonrpc":"2.0","method":"eth_accounts"}' http://127.0.0.1:8545

원래 위와 같이 나와야하는데 나는 그게 안되서 postman으로 통신을 하였다.

결과값은 이렇게 나온다.

{
    "jsonrpc": "2.0",
    "result": "0x56bc75e2d63100000"
}

여기서 result를 16진수에서 10진수로 변환해보면
16진수에서 10진수로 변환하기
https://ko.calcuworld.com/%EC%88%98%ED%95%99/16%EC%A7%84%EB%B2%95-%EA%B3%84%EC%82%B0%EA%B8%B0/

100000000000000000000 wei = 100ETH

1ETH = 10^18wei임을 알 수 있다.

즉 rpc통신을 하면 wei라는 단위로 보고, 우리는 그걸 변환해서 써야함을 알 수 있다. 왜 wei로 하냐면 코인은 소수점 거래가 가능하기 때문이다.

4. web3.js 라이브러리 통해서 rpc 통신하기

example.js파일을 만들어 준다.ganache는 틀어놓고 있어야 한다.

  • example.js
const Web3 = require('web3');
let connection = new Web3('http://127.0.0.1:8546');

connection.eth.getAccounts().then( data=>{
    //프로미스 객체를 반환한다.
    console.log(data);
})

이렇게 작성한 후 node example을 하면 example.js파일이 실행이 된다. 터미널 콘솔창을 보면 eth.getAccounts()는 프로미스 객체를 반환함을 알 수 있고, 또한, eth_accountseth.getAccounts로 쓰였음을 알 수 있다.

[
'0x02a599969D9CeBD2Abb732cBa97206aF0C92D827',
'0x2C8007676C007cD527E93B27d531D7d201C58e34',
'0xAA5AF10d54DE2488e107E151E80bB407c45866e9',
'0x8A6B18a79474090cB6C3589B4856F77199615F6E',
'0xD45c552Ad71220b874510D6Ec261474bAca74793',
'0x7f4c0910549A32422B200E825b2dA4e1bb575cCD',
'0xb8641E3ADbd054D7383256EE695B9D6C7fABbf83',
'0xfFa42448bEc7683433e420BAf69bd9490c7F5932',
'0x579f195ccba102b7471FDE43e193db0b18406D15',
'0x1C26b05e27C267abE999993c09ad23CBD817c0dc'
]

  • server.js
const Web3 = require('web3');
let connection = new Web3('http://127.0.0.1:8546');

//eth_accounts
connection.eth.getAccounts().then( data=>{
    //프로미스 객체를 반환한다.
    console.log(data);
})

//eth_getBalance
connection.eth.getBalance('0x02a599969D9CeBD2Abb732cBa97206aF0C92D827').then(
    data=>{
        console.log(data)
    }
)


여기서 getAccounts처럼 0x56bc75e2d63100000을 반환하지 않는다. 100000000000000000000을 반환하는 것을 알 수 있다.

web3는 rpc통신에서의 16진수를 사용자 편의를 위해서 10진수로 변환해서 반환한다.

web3.js는 원래 RPC 통신하는 부분을 기존에 request를 통해 작업했었던 것을 쉽게 사용할 수 있도록 해주는 라이브러리이다.
curl -X POST -d '{"jsonrpc":"2.0","method":"eth_accounts"}' http://127.0.0.1:8545
eth.getAccounts로 간단하게 표현시킬 수 있다.

<script src="~~/web3.js">를 통해 외부의 솔리디티를 가져올 수 있다. (?확실하지 않음.)

5.스마트 컨트랙트란?

(솔리디티) 코드를 RPC 통신을 통해 실행시킨다고 알고 있으면 된다.
솔리디티 코드를 실행시키려면 컴파일을 해야한다.
이 때,
hello.sol->컴파일 ->abi파일과 bin파일이 생성된다.

  • abi: Application Binary Interface
    런타임시 바이너리 코드와 데이터를 실행시키기 위한 JSON파일

  • bin: 바이너리 파일로 결과물을 준다.

    abi랑 bin을 코드를 실행시키는 키값이라고 대강 생각하자.

    1.솔리디티 코드를 작성한다.(visual studio code에서 작성)

  1. 솔리디티 코드를 컴파일한다. (컴파일 도구가 필요하다->설치)

    5-1. 스마트 컨트랙트 환경 설정(컴파일러 설치)

    npm install -g solc
    solcjs --version

    나는 또 npx solcjs --version을 해야했다.
    여기서보면 0.8.9버전임을 알 수 있다. 버전에 따라 문법이 바뀐다. 21.10.11기준으로는 0.6버전이 제일 안정적인 버전이라고 알려져있다.

    솔리디티 언어를 편하게 쓸 쑤 있게 해주는 라이브러리이다. 예약어에 색깔이 칠해져서 코드를 작성하기에 편하다.

5-2. 스마트 컨트랙트 작성하기

  • example.js
pragma solidity ^0.8.0;

contract hello {
    string value;//전역변수임.
    constructor(){
        value = "hello world";
    }
    function get() public view returns (string memory){
        /* string memory
        version up되면서 생긴 문법
        returns에서 파일 시스템 파일에 저장된 내용을 가져올거냐(storage) 
        메모리에 저장된 내용을 가져올건지(memory)*/
        return value;
    }

}

hello라는 contract를 만드록, 그 안에 value값으로 hello world라는 값을 넣는다. 그리고 퍼블릭 함수를 만들어서 value값을 반환한다.

returns (string memory)문법은 최근에 생긴것인데, storage를 반환할 것인지 memory값을 반환할 것인지 명시해주어야 한다.

5-3. 컴파일하기

npx solcjs --bin --abi .\hello.sol을 터미널에 입력하여 컴파일을 한다.

이렇게 과정이 끝나면 파일 두개가 생긴다.

[파일명][확장자][컨트랙트명].bin

hello_sol_hello.abi와 hello_sol_hello.bin 파일이 생겼음을 알 수 있다.

5-4. 스마트 컨트랙트 실행시키기

스마트 컨트랙트는 web3를 통해 실행된다. web3의 코드를 작성하면서 스마트 컨트랙트를 불러와서 실행시킨다.

const Web3 = require('web3');
let connection = new Web3('http://127.0.0.1:8546');

//eth_accounts
connection.eth.getAccounts().then( data=>{
    console.log(data);
})

//eth_getBalance
connection.eth.getBalance('0x02a599969D9CeBD2Abb732cBa97206aF0C92D827').then(
    data=>{
        console.log(data)
    }
)

const ABI_CODE =  JSON.parse('[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"get","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]');
//abi파일에 있는 것을 모두 복사해서 넣어준다.

const BYTECODE = '608060405234801561001057600080fd5b506040518060400160405280600b81526020017f68656c6c6f20776f726c640000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610166565b82805461006e90610134565b90600052602060002090601f01602090048101928261009057600085556100d7565b82601f106100a957805160ff19168380011785556100d7565b828001600101855582156100d7579182015b828111156100d65782518255916020019190600101906100bb565b5b5090506100e491906100e8565b5090565b5b808211156101015760008160009055506001016100e9565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061014c57607f821691505b602082108114156101605761015f610105565b5b50919050565b610232806101756000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80636d4ce63c14610030575b600080fd5b61003861004e565b6040516100459190610179565b60405180910390f35b60606000805461005d906101ca565b80601f0160208091040260200160405190810160405280929190818152602001828054610089906101ca565b80156100d65780601f106100ab576101008083540402835291602001916100d6565b820191906000526020600020905b8154815290600101906020018083116100b957829003601f168201915b5050505050905090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561011a5780820151818401526020810190506100ff565b83811115610129576000848401525b50505050565b6000601f19601f8301169050919050565b600061014b826100e0565b61015581856100eb565b93506101658185602086016100fc565b61016e8161012f565b840191505092915050565b600060208201905081810360008301526101938184610140565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806101e257607f821691505b602082108114156101f6576101f561019b565b5b5091905056fea264697066735822122053a2fbe5e470630734368f319a17b518c810f8853a5a073dee01dcd60ddc910964736f6c63430008090033';
//bin파일의 내용을 모두 복사해서 넣어준다.

const contract = new connection.eth.Contract(ABI_CODE)
//contract 메소드는 객체를 만들어주기때문에 new를 써주어야 함

//배포(코드를 실행) deploy

contract.deploy({
    data:BYTECODE
})
.send({
    from:'0x02a599969D9CeBD2Abb732cBa97206aF0C92D827',
    gas:'6712975'
   },(error,result)=>{
       console.log(error);
   }).then(data=>{
       console.log(data);
   })
//여러개를 인자값을 넣을 수 있는데, 객체 형태로 해주자.
//스마트 컨트랙트를 실행하면 GAS가 발생한다. 
//내가 가스르 발생시킬 주소값과  GAS로 얼마나 깎을건지를 넣어주어야 함.
//주소값은 공개키값으로
//단위가 주석값으로 


마지막 코드에서 2번째줄에 console.log(data)를 했는데 data안에 method가 있음을 알 수 있다.

위의 코드에서 deploy부분만 이렇게 바꿔주자.

contract.deploy({
    data:BYTECODE
})
.send({
    from:'0x02a599969D9CeBD2Abb732cBa97206aF0C92D827',
    gas:'6712975'
   },(error,result)=>{
       console.log(error);
   }).then(data=>{
       return data.methods.get().call()
       //call()은 프로미스 객체를 반환한다.
   })
   .then(result =>{
       console.log(result)
   })
//여러개를 인자값을 넣을 수 있는데, 객체 형태로 해주자.
//스마트 컨트랙트를 실행하면 GAS가 발생한다. 
//내가 가스르 발생시킬 주소값과  GAS로 얼마나 깎을건지를 넣어주어야 함.
//주소값은 공개키값으로
//단위가 주석값으로 

get안에 있는 값인 hello world가 잘 나온다.

5-4-1. options.address

options의 address가 무엇을 뜻하는지 알아보자.
지갑 주소를 알아보기 위해서 deploy부분만 또 바꾸어보자.

contract.deploy({
    data:BYTECODE
})
.send({
    from:'0x02a599969D9CeBD2Abb732cBa97206aF0C92D827',
    gas:'6712975'
   },(error,result)=>{
       console.log(error);
   }).then(data=>{
       console.log(data.options.address);
       //결과물에 대한 키값이라고 생각하면 된다. 
       return data.methods.get().call()
       //call()은 프로미스 객체를 반환한다.
   })
   .then(result =>{
       console.log(result)
   })


계속 ETH가 줄어들고 있다. 스마트 컨트랙트가 실행되고 있다는 뜻이다. 여기서 나오는 options.address는 영수증의 키값이다. (마치 블록체인의 txid?) 여기서는
0x73a8318D1472B9AF0BB685DDaef9b6F561207fc6가 영수증의 키값이다.

이걸 GAS를 발생시키지 않고 이 정보를 보는 방법도 있다.

5-5. 스마트 컨트랙트 내용 보기

deploy부분을 주석처리하고 다음과 같이 써보자.

// contract.deploy({
//     data:BYTECODE
// })
// .send({
//     from:'0x02a599969D9CeBD2Abb732cBa97206aF0C92D827',
//     gas:'6712975'
//    },(error,result)=>{
//        console.log(error);
//    }).then(data=>{
//        console.log(data.options.address);
//        //결과물에 대한 키값이라고 생각하면 된다. 
//        return data.methods.get().call()
//        //call()은 프로미스 객체를 반환한다.
//    })
//    .then(result =>{
//        console.log(result)
//    })

const helloContract = new connection.eth.Contract(ABI_CODE,'0x73a8318D1472B9AF0BB685DDaef9b6F561207fc6');

helloContract.methods.get().call().then(data=>{
    console.log(data);
});//call은 실행시킨다. 그 결과를 promise 객체로 반환한다. 

connection.eth.Contract(ABI_CODE,'영수증값(거래원장 인덱스)')를 helloContract로 선언한다. 이로써 블록체인을 배포시키지 않고, 스마트 컨트랙트 안에 담긴 내용을 볼 수 있다.

profile
코딩 재밌어요!

0개의 댓글