요 몇달 간 회사일에 치어 글쓰기에 너무 소홀했던 듯 하다. 각성하고, 이제 진도를 뽑아보자~

얼마전, Vitalik Buterin 과 Martin이 한국에 다녀갔다. 국내 블록체인 업체인 코인플러그도 퍼블릭 블록체인 기반의 사업성과가 기대에 못미쳐서인지 기업용 블록체인에 눈을 돌린 듯 하다. 자체 블록체인을 발표하고, Ethereum 과 협업을 하고있는 모습이 국내 블록체인 기술의 업그레이드를 위해 노력하는 모습인것 같아 다행이라고 생각한다.

너무 오랜만의 글이라 갈길이 머니 마지막 글에 이어 일단 web3.js 를 사용하는 방법에 대해 본격적으로 이야기 해보자.

▌web3.js 소개


github.com/ethereum 의 repository 목록에서 보이는 내용이다. web3.js 는 Ethereum Compatible JavaScript API 이다. 
일단 아래 그림을 보자.

예전에 외부에서 이더리움을 소개할 때 Ethereum 의 응용 구조에 대해 썼던 자료이다.현재 go Ethereum 의 경우, JSON RPC, IPC 외에도 그림에는 안나와있지만 WebSocket 을 지원한다.

위 구조를 보면 알겠지만, 이전 글까지는 Ethereum 의 JSON RPC 를 통해 어플리케이션이 Ethereum 을 사용하는 방법에 대해 소개 한 것이다.
그런데 JSON RPC 를 사용해서도 충분히 Ethereum 을 사용할 수 있겠지만, 응용 만드는 입장에서는 좀더 편하게 JSON RPC 를 호출해주는 라이브러리가 필요함을 느낀다. 특히 기본적인 Transaction 기반의 응용이 아니라 Smart Contract 를 사용하게 되면, Event 를 처리하거나 Smart Contract 에서 Return 되는 타입을 처리하여야 하는데, 이때 이러한 타입처리 또한 web3.js 라이브러리를 사용하면 편리하다.

web3.js 는 JavaScript 기반으로 Dapp 이나 서비스를 구현할 때 매우 유용하며, 현재는 EthereumJ 도 web3.js 를 지원하는 작업을 하고있다. 실질적으로 JSON RPC API 와 함께 Ethereum 의 표준 API 로 보면 되겠다. web3.js 는 내부적으로 HTTP 나 IPC 를 통해 JSON RPC API 를 호출하도록 되어있다.

▌JavaScript 코드에 web3.js 사용하기

web3.js 는 github/ethereum/web3.js 의 dist 내에 있는 web3.js 를 받아도 되지만,
Browser 에서 사용하려면 bower 를 사용하거나
Node.JS 를 사용한다면 npm 을 사용하면 된다.

이번에는 간단히 bower 로 install 한다.
[goodjoon eth_basic]$  bower install --save web3
crypto-js 와 bignumber.js 에 Dependency 가 걸려있으므로 두개를 추가로 받는다.
(npm 도 마찬가지로 web3 모듈을 설치하면 된다.)

이제 실제로 이전에 JSON RPC API 로 Transaction 보내기 했던 것을 web3.js 로 보내보도록 하고, 보너스로 블록 생성되는 이벤트를 받아보기 위한 Filter 를 하나 설치해보도록 하겠다.
화면은 간단히 아래와 같이 만들어 봤다.


일단 HTML 에서 web3.js 를 import 한다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="../bower_components/web3/dist/web3.js"></script>
    <title>Simple Wallet</title>

그다음, web3.js 를 사용하여 Ethereum Node 에 접속하려면, 아래와 같이 Provider를 지정 해주어야 한다.
var Web3 = require('web3');
var web3 = new Web3();
web3.setProvider(new Web3.providers.HttpProvider('http://localhost:8551'));

위 예제에서는 HttpProvider 를 지정해주었으나, IpcProvider 도 존재한다. Ethereum Node 와 같은 Machine 에서 동작한다면 IPC 로 Interface 하는것이 성능 측면에서 매우 유리하다.


▌Account 의 Balance 가져오기

Account 의 Balance 를 가져오기 위해서는 web3.eth.accounts 프로퍼티를 조사하면 된다. 
function updateBalance() {
    var address = web3.eth.accounts[0];
    var balance = web3.fromWei(web3.eth.getBalance(address), 'ether');

    $('#address').val(address);
    $('#balanceAmount').val(balance);
}

web3.eth.accounts[0]
web3.js API 들은 web3 라는 이름의 객체에 var web3 = new Web3(); 를 통해 할당하였다.
web3.eth 객체 내의 accounts 는 현재 Ethereum Node 에 생성된 계정들의 목록을 배열로 저장하고있다. 현재 내 Node 에 2개의 계정이 있으며, 이중 0 번 계정의 주소를 address 변수에 저장하였다.

web3.eth.getBalance(address)
특정 주소의 Balance 를 가져오는 함수이다. getBalance() 의 리턴값은 Wei 단위의 Balance 이다. address 의 Balance 를 가져와서 web3.fromWei() 함수를 통해 Wei 단위를 Ether 단위로 변경해서 balance 변수에 저장한다. 'ether' 를 지정하지 않아도 fromWei(), toWei() 함수의 기본 변환 단위는 'ether' 이다.

값을 가져와서 화면의 #address 와 #balanceAmount INPUT 필드의 value 로 표현한다.


▌Synchronous vs Asynchronous API

아마 위 함수들이 호출될 때 마다 브라우져나 Node.JS의 Console 에는 Synchronous XHR 은 deprecate 되었다고 경고가 출력될 것이다. 
web3.js 에는 Synchronous 함수와 Property 들이 있으며, 이에 상응하는 Asynchronous 함수들이 있다. 원칙적으로는 Asynchronous API 를 호출해야 한다. accounts 프로퍼티 대신 web3.eth.getAccounts(address, callback) 을 주도록 되어있으며, web3.eth.getAccounts(address, function(err, addresses) { console.log(addresses)}); 처럼 Callback 을 통해 결과를 받아야 한다.
(RPC API 는 deprecate 된 Synchronous 호출을 아직 실행 해주지만, IPC 방식은 아예 호출이 불가능하다)

▌Transaction 보내기

Ether 의 이체, Smart Contract 의 Deploy (Creation), Smart Contract 의 함수 실행 (Message) 모두 하나의 Transaction 발생 함수를 통해 실행한다. 바로 web3.eth.sendTransaction() 함수이다.
sendTransaction() 함수는 앞으로 지속적으로 사용할 것이며 사용법은 매우 간단하다.

web3.personal.unlockAccount(web3.eth.accounts[0],'1111');

일단, Page 가 로드되는 시점 즈음에서 account 를 unlock 해두도록 한다. 이전 RPC API 편에서도 이야기 했듯이, Account 를 사용하기 전에는 Account 를 Unlock 해주어야 한다. 만약 Ethereum 실행 시에 --unlock 옵션을 주고 --password <패스워드파일> 을 주어서 Node 의 BootUp 시에 Unlock 을 이미 하였다면 unlockAccount() 를 실행 할 필요는 없다.

Send 버튼을 눌렀을 때에 실행되는 코드는 아래와 같다.
var toAddress = $('#toAddress').val();
var sendAmount = web3.toWei($('#sendAmount').val(), 'ether');

var txHash = web3.eth.sendTransaction({
    from: web3.eth.accounts[0],
    to: toAddress,
    value: sendAmount
});

console.log(txHash);

web3.eth.sendTransaction(object) 함수에서 object 는 보낼 Transaction 의 내용이 정의 된 Object 이다.
     - from : Transaction 을 보내는 from 주소
     - to : Transaction 의 Destination 주소. 만약 Smart Contract Creation Transaction 인 경우, to 를 지정하지 않는다.
     - value : 보낼 Balance 의 양 (Wei 단위)
     - gas : 이번에는 Gas 양을 지정하지는 않았지만, Ethereum 은 기본적으로 90,000 Gas 를 적어 보내게 되어있다. data 필드를 사용하지 않는 기본 이체 Transaction 의 소모 Gas 는 현재 21,000 Gas 이다.

이 sendTransaction() 함수의 리턴값은 Transaction 의 Hash 값이다. Transaction 의 Hash 값은 Transaction 의 형식 자체에 오류만 없다면 Node 에서 즉시 리턴이 오는 값이다. Miner 나 타 Node 의 Validation 작업과는 무관하다. 

Transaction 이 Block 에 잘 묶여졌는지 확인하기 위해서는 
web3.eth.getTransactionReceipt(txHash) 로 Receipt 를 확인해야 한다. 만약 null 이 리턴되었다면 Transaction 은 Block 에 묶이지 않는 것이다.


▌Block Filter 

Ethereum 에서 새로운 블록이 나오거나 Smart Contract 의 Method 내에서 Event 를 실행(Trigger) 시켰을 때, 특정 Address 나 Topic 에 해당하는 Log 를 알림 받으려고 할 때 사용할 수 있는 기능이 바로 Filter 객체이다. 그중 기본적으로 최신 블록이 생성되어 Mined/Import 되었을 때 이벤트를 받을 수 있도록 하여 좀더 동적인 느낌이 나도록 해보겠다.

var blockFilter = web3.eth.filter('latest');
blockFilter.watch(function(error, blockHash) {
    var block = web3.eth.getBlock(blockHash);
    appendLog('New Block('+block.number+')['+block.hash+'] / ' + block.transactions.length + ' TXs');
});

위 처럼 web3.eth.filter() 함수를 통해 Block 에 대한 Filter 를 걸 수 있다. 'latest' 를 넣어주면 최신 생성된 블록이 있는 경우 Filter 로 잡히게 되며, 'pending' 을 적어주면 Pending 중인 블록 (마이닝 대상)을 보여준다. 또한 Smart Contract 의 Transaction Receipt 내의 Log 에 한하여 Address 나 Topic 을 옵션으로 하여 Filter 할 수도 있는데, 이건 다음번 Smart Contract 하면서 이야기 해보도록 하겠다.

이렇게 코드를 만들고 나면 최종 아래와 같은 화면을 볼 수 있다.


▌결과화면


이처럼 web3.js 를 사용하는 방법은 매우 간단하고 쉽다. 만약 Transaction 에 Custom 한 데이터를 포함하고 싶다면 sendTransaction() 시에 { data : '<HEX값>'} 을 추가하여 보내기만 하면 된다. Bitcoin 이 OP_RETUN 코드 실행으로 가져올 수 있는 40 Byte 의 임의의 데이터 공간만을 제공하는것에 비해 Gas 만 충분하다면 (1 Byte 당 5 Gas) 큰 데이터를 넣는것도 문제 없다. 실제로 수십KB 이상의 이미지 Binary 를 Homestead 와 같은조건으로 놓고 테스트해 본 결과 잘 들어간다. 

다음은 Smart Contract 에 대한 개념과 개발 방법을 시작으로, Smart Contract 에 대하여 본격적으로 연재 해보도록 하겠다.



반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
이전의 JSON RPC API 기본의 연장이다.
이번에는 JSON RPC 를 통해 실제 Ether 를 이체시키는 Transaction 을 발생시켜보자.

▌Unlock Account
Geth 와 Eth 모두 Account 로 부터 Transaction 을 발생시키기 위해서는 From 이 되는 Account 를 unlock 해야 한다. 
Account 를 Unlock 하기 위해서는 Account 를 생성했을 당시 입력한 패스워드를 반드시 알고 있어야 한다.

일단, 아래와 같이 Request 를 한다.
{
    "jsonrpc":"2.0",
    "method":"personal_unlockAccount",
    "params": [
        "0x2c6191a4792a3a33cbd2f8b7b7e583a61fb33b23",
        "1111",
        0
        ],   
    "id":100
}
"params" Object 의
1번째 파라미터는 "Unlock 을 하고자 하는 자기 Account 의 주소"이다
  Wallet 안에 해당 Account 가 존재해야 하며, Wallet 의 위치는 datadir 에 따라 다르므로, geth account list 나 JSConsole 또는 RPC 로 현재 자신의 Wallet 내의 account 를 확인한 후 입력하자
2번째 파라미터는 "Account 의 Password" 이다
3번째 0 은 Unlock 의 Timout 을 "초" 단위로 입력한다.
  0 으로 지정하면, 이번 Session (Geth 의 Instance 가 살아있는) 동안은 계속 unlock 하게 된다.

성공 했다면 아래와 같은 response 가 온다
{
  "id"100,
  "jsonrpc""2.0",
  "result"true
}


▌Ether 보내기
일단 다른 Node 에 Account 를 하나 추가하였다. Address 는 "0xd33cbaced7b3990554667391708350cbbe3083a6" 로 EOA 를 생성했고, 아직 balance 는 0 이다.
2c6191a4792a3a33cbd2f8b7b7e583a61fb33b23 계좌에서 위 계좌로 150 ether 를 이체 해보겠다. 
Transaction 을 보낼때는 Wei 단위를 쓰며, 150 이더는 150,000,000,000,000,000,000 Wei 이다. 이걸 HEX 값으로 바꾸면 0x821ab0d4414980000 이 된다.

{
    "jsonrpc":"2.0",
    "method":"eth_sendTransaction",
    "params": [{
      "from""0x2c6191a4792a3a33cbd2f8b7b7e583a61fb33b23",
      "to""0xd33cbaced7b3990554667391708350cbbe3083a6",
      "value""0x821ab0d4414980000"
    }],   
    "id":100
}

위 처럼 Request 를 보내면, 정상적인 Transaction 이 발생 되었다면 아래와 같은 응답이 온다.

{
  "id"100,
  "jsonrpc""2.0",
  "result""0x265be2659d440ccd894370a9354bb189123f71ad17fea55f3b7dc2f8900d1664"
}

Result 로 보내준 값은 Transaction 의 Hash 이다.

▌Receipt 조사하기
Ethereum 은 Miner 가 생성한 Block 을 Import할 때 Transaction 을 처리하였다면, Receipt 를 만들어서 저장한다. 그래서, Transaction 이 정상적으로 Block 으로 묶여서 처리되었다면 Receipt 가 생기게 되어있다. Receipt 에는 해당 Transaction 처리에 소모된 Gas 와 Transaction 이 묶인 Block 의 번호 등이 포함되며, Smart Contract 를 Create 한 경우, Contract Account 의 Address 도 들어가게 된다.

Receipt 요청
{
    "jsonrpc":"2.0",
    "method":"eth_getTransactionReceipt",
    "params":[
        "0x265be2659d440ccd894370a9354bb189123f71ad17fea55f3b7dc2f8900d1664"
        ],
    "id":100
}

Receipt 결과
{
  "id"100,
  "jsonrpc""2.0",
  "result": {
    "transactionHash""0x265be2659d440ccd894370a9354bb189123f71ad17fea55f3b7dc2f8900d1664",
    "transactionIndex""0x0",
    "blockNumber""0x7a7",
    "blockHash""0xb760b2d919227cf557f7289c3a5181f09fb0deda669df35b50c510af189cdc5a",
    "cumulativeGasUsed""0x5208",
    "gasUsed""0x5208",
    "contractAddress"null,
    "logs": []
  }
}

해당 Transaction 이 Mining 되어 Block 들어갔고, Block Hash 도 보여준다.

이제 잔고를 확인해보면 된다.

▌잔고 확인하기
위 이체 작업이 잘 되었는지 잔고를 확인해보도록 하겠다.

Balance 확인 요청
{
    "jsonrpc":"2.0",
    "method":"eth_getBalance",
    "params": [
        "0xd33cbaced7b3990554667391708350cbbe3083a6"
        ],   
    "id":100
}

Balance 확인 응답
{
  "id"100,
  "jsonrpc""2.0",
  "result""0x0821ab0d4414980000"
}

위 처럼 150 ether 가 잘 이체되었음을 알 수 있다.


이렇게 JSON RPC API 를 사용한 방법은 아주 간결하다. 다만 Bitcoin 과 프로세스 상 다른부분들이 있어서 까다로워 보일 뿐이다.
이제 기본적인 Transaction 발생까지 해보았으니, 다음부터는 Web3 API 나 RPC 를 사용한 Smart Contract 로 들어가볼까 한다.

반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
블록체인에 대한 개념과 사토시 나카모토에 대한 이야기를 할 때 빠지지 않는 코스로 잠깐이라도 비잔틴 장군 문제 혹은 비잔티움 장군 문제, 비잔틴 오류 허용 에 대해 이야기가 나오곤 한다. 그런데 대부분 '아 무언가 신뢰할 수 없는 환경에서 어려운 문제를 풀게 해서 신뢰를 얻어내게 했구나' 정도로 이해하면 다행이고, 설명하는 사람들 조차도 어떤 과정으로 그러한 신뢰를 갖게 하였는지에 대해서는 상세히 모르고 설명하는 케이스가 대부분이었다. 

아래는 PoW

▌두 장군의 문제

불확실한 통신 상황에서 상호 동기화를 시도할 때의 문제점으로,
이건 중간의 통신 장애가 있는 경우 어떻게 공격 시간을 전달할것인가라는 문제이며 아직까지 이에 대한 해결방안은 나오지 않았다.

위 처럼 장군 A1, A2 는 가운데 B 에 있는 적들을 공격해야 하는데, 동시 공격하지 않으면 이길 수 없는 상황이다.
A1, A2 사이를 흐르는 강이 있으나, 건너는 경우 연락병이 포획될 가능성이 있다.
이러한 상황에서, A1, A2 는 시간을 합의하고 상호 확답을 받아야 한다. 

이 문제는 비잔틴 장군의 문제와는 다른 문제이다. 


▌비잔틴 장애허용, 비잔틴 장군 문제

300명의 병력이 있는 비잔틴 성을 100명씩의 병력을 가진 장군 5명이 치려함
이기려면 적 병력보다 많은 병력이 공격해야 함
장군들은 연락병을 보내 소통 가능함
장군들 중에는 배신자가 있어 서로 신뢰 불가함

문제) 서로 신뢰할 수 없는데, 어떻게 공격 시각을 합의할 수 있는가? 
     . 가정 1 - 공격 시각은 상관 없음. 모두 한번에 공격할 수 있는 합의된 시각이면 됨
     . 가정 2 - 배신자는 병력을 분산시키려 하므로 이전 장군의 메시지와 다른 시각을 제시함
     . 가정 3 - 각 장군은 자신의 바로 다음 장군에게만 연락할 수 있음
     . 가정 4 - 배신자도 지정한 시간에 공격에 가담 함


<<문제 생기는 상황>>
  1. @장군1 이 "9 AM" 공격시각을 적어 서명과 함께 @장군2 에게 보냄 
  2. @장군2 는 @장군1 의 "9 AM" 공격시각을 보고 기억한 후 @장군3 에게 전달함
  3. @장군3배신자로, "9 AM" 메시지를 찢어버리고 "8 AM" 으로 고쳐서 @장군 4에게 전달함
  4. @장군4 는 "8AM" 을 기억한 후 @장군5 에게 전달
  5. @장군5 는 "8AM" 을 기억
  6. 8AM 에 @장군3, @장군4, @장군5 가 쳐들어가지만 300명이므로 지게 됨

<<PoW 블록체인 해결>>
[새로운 규칙]
A. 장군은 메시지를 보내기 위해 반드시 10분의 시간을 들여야 함
B. 메시지는 모든 이전 장군의 메시지와 10분의 시간을 들였다는 증거를 포함하여 보내야 함

  1. @장군1 이 "9 AM" 공격시각을 적어 10분간 작업하여 증거와 함께 @장군2 에게 보냄
  2. @장군2 는 "9 AM" 메시지와 @장군1 의 10분 작업 증거를 보고 확신 후, @장군3 에게 "9 AM" 메시지 보냄
    1. @장군2 도 10분 간 작업 함
    2. @장군1, @장군2 의 메시지와 작업내용 모두를 포함하여 보냄
  3. @장군3배신자로 "8AM" 으로 메시지를 수정하여 보내고 싶으나 그냥 보낼 수 없음. 아래 중 택 1 해야 함.
    1. 속일 수 있는 유일한 방법
      1. @장군3 은 10분보다 빠르게 작업을 하여 "8 AM" 메시지를 만들어 냄
      2. @장군1, @장군2 의 총 20분 작업에 해당하는 메시지 모두를 남은 시간 내에 만들어 포함 시켜 보냄
    2. 안들키고 있으려면 "9 AM" 으로 보내기
  4. @장군4, @장군5 모두 동일

[의미]
  1. 만약 @장군3 이 "8AM" 으로 보냈다면, 모두 장군 3이 거짓임을 알게 됨
  2. 만약 @장군2 가 배신자였다면 @장군2,3,4,5 모두 9AM 공격 가므로 이기게 됨
  3. 만약 @장군1 이 배신자였다면 8AM 에 모두 함께 공격 가므로 이기게 됨


비잔틴 장군의 문제를 PoW 를 통한 블록체인 기술로 해결 한 예시이다. 블록체인은 "이전 메시지를 포함" 시킨 새로운 메시지로 이해할 수 있으며, 포함된 이전 메지시를 변경한 경우 변경(위조)되었다는 사실을 바로 알 수 있는 장부이다. PoW 는 "10분의 시간을 들여 메시지를 만드는 과정" 이며, 정말 그러한 작업을 하였는지를 단번에 검증해낼 수 있는 방법을 제공한다.
그러나 10분의 시간을 들이지 않고는 절대 새로운 메시지에 포함된 이전 메시지를 다시 만들어낼 수는 없는 알고리즘이며, 비잔틴 장군의 문제 해결에 가장 핵심이 되는 메커니즘인 것이다. 






반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,