스마트 컨트랙트 ~ 1 (23/05/19)

nazzzo·2023년 5월 22일
0

스마트 컨트랙트 기초


  • 스마트 컨트랙트란?

스마트 컨트랙트는 계약의 내용과 실행 조건을 코드를 통해 사전에 설정한 후
해당 조건이 충족되면 블록체인 네트워크에서 자동적으로 계약을 실행하는 기능을 의미합니다

이 때 계약의 조건과 실행 결과는 블록체인에 영구적으로 기록되며 변경이 불가능하고 검증도 가능합니다
즉, 중개자 없이 계약 당사자끼리도 신뢰도 높은 거래를 할 수 있게 해주는 것이 스마트 컨트랙트의 핵심입니다

다만 스마트 컨트랙트의 실행에는 트랜잭션 처리에 필요한 수수료인 가스가 발생합니다
트랜잭션 발동 시점은 가스 소모량에 따라 달라지는데, 일반적으로는 비트코인보다는 훨씬 빠른 속도로 처리됩니다
대신 코드 연산의 복잡도에 따라 그 비용이 높아질 수 있다는 것이 문제점으로 꼽힙니다

현재 날짜 기준으로 이더리움의 시세는 대략 230만원대...가스는 개발 단계에서도 큰 문턱으로 다가옵니다

그래서 다음과 같은 도구들의 도움을 받을 필요성이 있습니다



Ganache

Ganache

  • 가나쉬(Ganache)는 로컬 환경에서 스마트 컨트랙트를 개발 및 테스트하는 데 사용하는 도구입니다
  • 로컬에서도 실제 블록체인 네트워크와 유사한 환경에서 스마트 컨트랙트의 성능 측정과 디버깅이 가능합니다

그러니까 가나쉬를 사용하면 공짜로 가스 소모량 측정이 가능하다는 것
가나쉬는 브라우저와 노드 양쪽 환경을 모두 지원합니다


설치 및 실행

npm install ganache-cli
npx ganache-cli

실행 명령어 입력 즉시 터미널에 다음과 같은 정보가 출력됩니다
(사용 가능한 어카운트, 각 어카운트의 비밀키, 지갑의 니모닉키, 가스 한도와 소모량 등...)

Ganache CLI v6.12.2 (ganache-core: 2.13.2)

Available Accounts
==================
(0) 0x5f08A8f707aE3B150FC1D83DcD08c7885a11489e (100 ETH)
(1) 0x49Af8F9c3819780Dc8dE49E8Cf1f781E228fA250 (100 ETH)
(2) 0xCEa401ebd7Fe8da7A7E9DeFcC44dbbDaf27D7A58 (100 ETH)
(3) 0xec460E0AE6B5EdbF57EA6252BE4Ab1c72f9276aa (100 ETH)


Private Keys
==================
(0) 0xe4ed3ba8b67a682bf55b2b2539d60c9b493f2d392900897a37eab0d3b50c550a
(1) 0x4c604418f5d04db0165945d0140a448beb3e064bc9a707398c13f75cf412d768
(2) 0x42011cd9c22b231289f95ab873960d52301bc44caaac2ccadbb507bf8c15035a
(3) 0x9c1df16b0bf9dd16b6d357cded344268324bf41e2494775cd7dd5496dee85e7e


HD Wallet
==================
Mnemonic:      vehicle duck brain sock open pigeon crew february quit submit meat lawsuit
Base HD Path:  m/44'/60'/0'/0/{account_index}

Gas Price
==================
20000000000

Gas Limit
==================
6721975

Call Gas Limit
==================
9007199254740991

Listening on 127.0.0.1:8545

요청 & 응답 테스트 해보기

// 요청
curl -X POST --data '{"jsonrpc":"2.0", "method":"web3_clientVersion", "params": []}' http://127.0.0.1:8545

// 응답
{"jsonrpc":"2.0","result":"EthereumJS TestRPC/v2.13.2/ethereum-js"}

블록체인 네트워크(가나쉬)에의 요청은 포스트맨으로도 가능합니다
단, 요청 내용은 전부 바디 영역에 담아야 합니다

// 연결된 모든 어카운트 요청
// 127.0.0.1:8545
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": []  
}

// 응답
{
    "jsonrpc": "2.0",
    "result": [
        "0x20eb301037015bc07f2ef5c5ba04ef75d784c2c4",
        "0x3c955d6d8debb3f8484fc45fe59dfee6fd710e21",
        "0xefaf2bc284b85e9974859d751548fb7099c05d2e",
        "0x357b3730a02747c6ad6503262563888f94f2897b",
        "0x552194de28e590bdebe6fa3a34d1428f219d10f4",
        "0x77a9feca7b55ca87d50a6d3e8e4860cf92efe3ce",
        "0xf29ac78ce5575c9a6b40c5505559445f0daf43af",
        "0x31facca9a1613001a07b65ad7f63a632d2cc4cae",
        "0x9149c54786091c66c1abde33fc5930184b662e27",
        "0x3d51f729c037b13fe06f5e12b152dd1030ddafc0"
    ]
}

// 어카운트의 밸런스 요청 (인자가 필요한 요청을 보내려면 params에 담아야 합니다)
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": ["0x20eb301037015bc07f2ef5c5ba04ef75d784c2c4"]  
}


// 응답 (wei 단위, 10진수로 변환해서 10**18로 나누면 100eth가 됩니다)
{
    "jsonrpc": "2.0",
    "result": "0x56bc75e2d63100000"
}


// 10ETH를 송금하는 트랜잭션 요청 
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [{
      "from" : "0x136F87E6DbB4BDf620154F45957Df4D0c08dA0e5",
      "to" : "0x55d179aDE01ca621D844f6883C75b7c6A6837589",
      "value" : "10000000000000000000" 
  }]
}

// 응답
{
    "jsonrpc": "2.0",
    "result": "0xcb42a205f58a88e7c5aaa4118f598052274a115a5c1b66240c6bcb4d898fcac5"
}

블록체인도 결국은 요청과 응답이라는 것.

sendTransaction? sendRawTransaction?

두 메서드의 차이는 서명 방식의 차이입니다
sendTransaction 은 트랜잭션 객체를 노드에 보내면 노드에서 자동으로 서명을 진행하는 반면,
sendRawTransaction은 직접 트랜잭션을 생성, 서명해서 블록체인 네트워크에 전송하는 방법입니다
더 많은 제어권과 유연성을 지녔지만, 트랜잭션의 구성과 서명을 수동으로 처리해야 하기 때문에 좀 더 복잡합니다


가나쉬의 사용법에 대해서는 일단 여기까지만 다루겠습니다



web3

web3

  • web3는 브라우저 환경에서 rpc 통신(블록체인 네트워크와의 소통)을
    좀 더 간편하게 사용하기 위해 고안된 라이브러리입니다
    (노드 환경에서도 사용 가능, 사용성 측면에서 axios와도 비슷한 면이 있습니다)

  • web3는 이더리움 재단에서 직접 만들어서 배포중이며, 유사한 기능을 가진 다른 사설 라이브러리(ethers)도 있습니다


cdn으로 web3 사용해보기

주요 메서드는 인스턴스의 eth객체 안에 담겨있습니다
그리고 대부분의 함수는 프로미스를 반환합니다

<script src="https://cdn.jsdelivr.net/npm/web3@1.10.0/dist/web3.min.js"></script>

const web3 = new Web3("http://127.0.0.1:8545")
  
// 응답은 프로미스 객체로 반환
web3.eth.getAccounts().then(console.log)
/**
  ['0x136F87E6DbB4BDf620154F45957Df4D0c08dA0e5',
 '0x55d179aDE01ca621D844f6883C75b7c6A6837589', 
 '0x68971c9A476Ed4BDd77bb38003d64d4be20CFB6d', 
 '0x5da8D9190d6ec13b1fa43Ce9F1F983b046044997', 
 '0xaac778BBfaeDc719ba4Ba29Ac5Bde920da0a4815', 
 '0x26333B7C59955A404c5f926113ec15F07EBD1E4F', 
 '0xbDB2A53faBF9103035A61277e62356582b545896', 
 '0xA3B8498B44DFb12554da947AA8a6f72d7e0b5330', 
 '0x16FC300093e4dd3c67eaA26d485221DEd48bc2BF', 
 '0x82314Aa618A625c5F35390C0CB279bB29D1dF876'
]
**/

// eth_getBalance
web3.eth.getBalance("0x136F87E6DbB4BDf620154F45957Df4D0c08dA0e5").then((data)=> {
  console.log(data) // 84999160000000000000 ~ 현재 잔액

  const value = web3.utils.fromWei (data)
  console.log(value) // 84.99916 ~ wei => eth 단위 변환
}

// eth_sendTransaction
web3.eth.sendTransaction({
    from:"0x2c3338e54FE9B1Eb3e53DEc6837ebB27EC9388D6",
    to:"0xb3c52A71e05cA5A806dA789944a9A462f0eA3D38",                   
    // 보낼 값을 스트링으로 전달해야 합니다  
    value: web3.utils.toWei("5", "ether")
}).then(console.log)

/**
{
  transactionHash: '0x06db6726dc73c17cc5dca952ced7829082e31dc520ded87c3fa2806d630d609d',
  transactionIndex: 0,
  blockHash: '0x7aea3cb8ea9564ee32b881086a437f0cd1e45e8004c3e2e307b0576cf9e9d611',
  blockNumber: 1,
  from: '0x2c3338e54fe9b1eb3e53dec6837ebb27ec9388d6',
  to: '0xb3c52a71e05ca5a806da789944a9a462f0ea3d38',
  gasUsed: 21000,
  cumulativeGasUsed: 21000,
  contractAddress: null,
  logs: [],
  status: true,
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
}
**/



노드 환경에서의 사용법

// npm 설치
npm install web3


// index.js
const Web3 = require("web3")
const web3= new Web3("http://127.0.0.1:8545")

web3.eth.getAccounts().then(console.log)
web3.eth.getBalance("0x136F87E6DbB4BDf620154F45957Df4D0c08dA0e5").then(console.log)

// 이하 생략

web3를 쓰는 것이 RPC통신보다는 훨씬 간편하기는 하네요



끝으로 promise.then(onFulfilled, onRejected) 잠깐 복습!

.then은 두개의 콜백함수를 인자로 받습니다

  • onFulfilled: 프로미스가 성공적으로 처리되었을 때 실행할 콜백 함수
  • onRejected: 프로미스가 실패했을 때 실행할 콜백 함수

다만 두번째 인자는 잘 사용하지 않습니다
에러 처리를 위해서는 별도의 .catch 함수를 사용하는 것이 보편적

promise.then((result) => {
  // 성공적으로 처리되었을 때 실행할 코드
  console.log('결과값:', result);
}).catch((error) => {
  // 실패했을 때 실행할 코드
  console.error('에러:', error);
});

// 사용성은 같지만 명시적인 콜백함수를 사용하는 것을 추천
.then(console.log) = .then((result) => {console.log(result}) 



0개의 댓글