geth 소스 읽기 Part2 Day01
kr·@woojin.joe·
0.000 HBDgeth 소스 읽기 Part2 Day01
 이 글은 go-ethereum(geth 클라이언트) 소스 읽기 시리즈 Part2 연재 중 첫 번째 글입니다. 전체 연재 목록은 아래 페이지에서 확인해 주세요. http://www.notforme.kr/block-chain/geth-code-reading ## Part2 연재의 대상 독자 및 목표 이 글은 독자 분들이 적어도 Java와 같은 OOP 계열의 언어로 프로그래밍 경험이 있다는 것을 가정합니다. 또한 계정, 채굴 등 블록체인과 이더리움과 관련된 기초적인 개념과 이더리움 백서나 황서의 내용을 간단하게 알고 있다고 가정합니다. 연재의 목표는 `geth` 코드를 읽으면서 백서 및 황서에서 정의된 스펙이 어떻게 구현되었는지를 확인하는 것입니다. 더불어 이 연재는 다음 3가지 목적을 염두하고 쓴 것입니다. 1. 새로운 언어(`Go`)를 오픈소스 코드를 읽으며 배운다. 2. 오픈소스를 읽으며 코드리딩 능력을 배양한다. 3. 블록체인의 기술을 직접 코드를 통해서 익힌다. 지난 Part1에서는 총 6번의 글을 통해서 `geth`구조와 실행방법에 대해서 살펴봤습니다. Part2에서는 좀 더 구체적으로 이더리움의 핵심 기능들이 어떻게 구현되었는지 Part1에서와 마찬가지로 코드를 통해서 알아보려고 합니다 ## 다루는 내용 오늘은 블록체인의 꽃인 마이닝 코드를 분석하기 위한 준비 작업으로 실습을 해봅니다. 로컬 환경에 `geth`를 실행한 후 마이닝을 해보고 콘솔로 접속하여 사용자의 잔액 확인, 이더리움 전송하기 등을 실습해 볼 예정입니다. 오늘 이 글의 실습이 실제 마이닝 과정이 코드에서 어떻게 진행되는지 살펴보는데 도움이 될 것입니다. 오늘 실습은 지난 Part 1의 연재에서 VS Code를 사용했던 것과 달리 Jetbrains에서 나온 Goland를 IDE로 사용하였습니다. 실습을 따라해 볼 분들은 [사이트](https://www.jetbrains.com/go/)에서 30일 무료 평가판을 다운 받아서 사용하시면 됩니다. --- ## Goland IDE에서 디버깅 모드로 실행하기 소스를 다운받아서 Goland로 프로젝트를 열면 소스에 `main` 함수가 있을 경우 아래와 같이 실행버튼이 보이게 됩니다.  <br> 지난 Part1에서 분석한 `geth` 커맨드의 최초 진입점이 되는 `main` 함수의 실행 버튼을 클릭하면`geth`를 `Run` 또는 `Debug`로 실행 시킬 수 있습니다.  <br> 하지만 이대로 실행하며 다음과 같은 오류를 만나게 됩니다.  <br> 이는 `main.go` 파일 하나만 참조해서 빌드하려다보니 참조에러가 난 것인데요. Goland의 실행환경 셋팅을 변경해 주면 해결할 수 있습니다. 메뉴 Run > Edit Configuration을 실행하면 방금 실행에러가 났던 `Go Build` 환경이 있을 것입니다. 이 환경을 선택한 후에 실행 방법을 수정해야 합니다.  <br> 실행오류가 났던 설정은 `Run kind`가 파일로 선택되어 있을텐데요. 이를 위 그림과 같이 디렉토리로 변경하고 `main.go`가 있는 `cmd/geth` 인 것을 확인해야 합니다. 추가로 `Program arguments`에는 터미널에서 `geth`를 실행할 때 전달하는 옵션을 동일하게 줘야 합니다. 여기서는 프라이빗 네트워크로 실행할 것이기 때문에 `—nodiscover`를 줬습니다. 이제 다시 실행하면 정상적으로 `Goland`에서 `geth`를 직접 실행되는 것을 확인할 수 있습니다. ## 로컬 테스트 네트워크 실행 로컬 테스트 네트워크 환경은 이더리움 공식 문서에 있는 [Test Networks](http://www.ethdocs.org/en/latest/network/test-networks.html#id3)를 참조하여 진행합니다. 매뉴얼을 기반으로 실습에서 로컬 네트워크를 위해 필요한 것은 아래와 같습니다. 1. 커스텀 genesis.json 파일 2. 커스텀 Data 디렉토리 공식 매뉴얼에는 커스텀 네트워크 ID를 요구하고 있지만 여기서는 `--nodiscover` 옵션을 기본으로 줄 예정이라 커스텀 네트워크ID를 생략해도 됩니다. 그럼 차근히 로컬 네트워크를 구축해 봅시다. ### genesis.json 준비 `genesis.json`은 테스트 네트워크의 블록체인의 첫번째 블록 정보를 담고 있습니다. 공식 매뉴얼에 샘플이 있습니다만… 최신버전을 따라가지 못해서 현재 버전의 `geth`에서는 사용할 수 없습니다. 매뉴얼에 빠져 있는 `json`의 키 중에 하나는 `config`입니다. `config`가 포함된 커스텀 genesis.json 파일이 아래와 같이 준비되어야 합니다. ```javascript { "config": {}, "nonce": "0x0000000000000042", "timestamp": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "0x00", "gasLimit": "0x8000000", "difficulty": "0x400", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x3333333333333333333333333333333333333333", "alloc": {} } ``` \ 각 속성에 대한 상세한 내용은 (비록 영어지만) 다음 링크의 설명으로 위임하겠습니다. > https://gist.github.com/0mkara/b953cc2585b18ee098cd#file-genesis-md ### 커스텀 디렉토리에 genesis.json 초기화 이제 `genesis.json`이 준비 되었으니 이 파일로 테스트 네트워크의 블록체인을 초기화할 준비가 되었네요. 초기화 커맨드는 굳이 IDE에서 디버깅 모드로 실행하지 않고, 이미 컴파일한 `geth` 바이너리로 다음과 같이 명령을 입력하여 초기화 합니다. ```shell geth --datadir ./node1 init genesis.json ``` \ 이 명령 가운데 중요한 부분은 `--datadir ` 옵션과 함께 커스텀 디렉토리의 경로를 넘긴 것입니다. 명령이 정상적으로 실행되었다면 `Successfully wrote genesis state`로그를 볼 수 있고 커스텀 디렉토리로 준 경로 `node1`에 따라 실제 네트워크의 데이터가 저장될 `node1` 디렉토리가 생긴 것을 확인할 수 있습니다. ### 테스트 네트워크 설정으로 geth 실행 이제 `Goland`에서 `geth`를 실행할 차례입니다. 앞서 `Program Arguments`에 `nodiscover` 옵션 하나만 주었는데요. 테스트 네트워크의 노드로 실행하기 위해서 공식 매뉴얼에 따라 다음과 같이 몇가지 옵션을 추가합니다. ``` --identity "node1" --nodiscover --rpc --rpcapi "db,eth,net,web3" --rpcport 8545 --rpccorsdomain "*" --datadir "커스텀 디렉토리 경로" ``` \ 새로 추가된 옵션은 `geth`를 컨트롤하기 위한 `rpc`관련 설정들입니다. 이는 바로 이어서 마이닝에 따라 블록 생성과정을 확인할 때 필요한 설정이기도 합니다. 위 옵션을 추가했다면 이제 `Goland`에서 `geth`를 디버깅 환경으로 실행합니다. 역시 문제가 없다면 `geth`가 실행된 상태로 다음과 같이 대기하고 있게 됩니다.  <br> ## 콘솔로 접근하여 마이닝 시작하기 이제 `geth` 노드 하나가 IDE의 디버깅 환경으로 실행된 상태입니다. 이 상태에서 터미널을 하나 열어서 해당 프로세스에 명령을 줄 수 있도록 콘솔로 접근합니다. 콘솔 접근은 간단합니다. 현재 실행 중인 `geth`의 커스텀 디렉토리를 동일하게 맞춰서 다음과 같은 명령으로 콘솔을 실행합니다. ```shell geth attach --datadir ./node1 ``` \ 정상적으로 콘솔이 실행되면 `>`와 같은 프롬프트가 보입니다. 이 프롬프트는 자바스크립트 런타임 환경이라고 보면 됩니다. 이더리움은 `geth`의 명령을 `rpc` 형태로 제공하고 있고 `web3`라는 자바스크립트 라이브러리를 사용하여 이더리움의 `rpc`를 손쉽게 호출할 수 있는데요. 이 때문에 `geth`에서 자바스크립트 런타임환경을 제공하고 있는 것입니다. 이제 이 프롬프트를 통해서 `web3 `의 명령을 입력할 수 있습니다. 한 번 마이닝을 시도해 볼까요? 다음 명령을 콘솔에 입력합니다. ```she > miner.start() ``` \ 안타깝게도 이 명령은 다음과 같은 에러와 함께 실행되지 않습니다.  <br> 마이닝을 통해서 생성된 이더리움을 귀속시킬 계정인 `etherbase`가 없기 때문입니다. 그럼 계정을 하나 만들어 봅시다. 다음 그림과 같이 `web3`의 `personal.newAccount()` 함수를 이용하면 손쉽게 계정을 만들 수 있습니다.  <br> 이제 다시 miner.start()를 호출하면 정상적으로 호출이 성공하고 IDE의 로그 창에서 다음과 같은 로그를 확인할 수 있습니다.  <br> 블록 생성에 따라 획득한 이더리움이 위에서 생성한 계정으로 들어온 것을 확인할 때도 역시 `web3`를 사용하면 됩니다. 예를 들면 다음과 같이 확인할 수 있습니다.  <br> `eth.getBalance()` 함수는 사용자 계정의 잔액을 반환하는데 반환된 값의 단위가 이더리움이 아닙니다. 따라서 `web3.fromWei()`함수로 단위변화를 해야 이더리움 단위로 잔액을 조회할 수 있습니다. 마이닝을 중지하려면 콘솔에 `miner.stop()` 을 실행하면 됩니다. 제 실습 장비는 맥북프로인데 마이닝을 켜고 한 5분정도만 지나면 금방 팬돌아가는 소리가 납니다. PoW 과정에서 CPU소모가 있으니 마이닝 과정 확인이 필요할 때만 실행시키면 됩니다. ## Mist로 테스트 네트워크의 계정 확인하기 Mist는 이더리움 재단에서 관리하는 지갑 및 Dapp 브라우저입니다. 여기서는 위에서 생성한 계정의 정보를 확인하고 스마트 컨트랙트를 간단히 확인해 볼 용도로 설치해서 사용해 보려고 합니다. 다음 링크에 들어가 해당 운영체제에 맞는 바이너리 파일을 받아서 설치합니다. > https://github.com/ethereum/mist/releases 이제 Mist를 실제 이더리움의 메인네트워크가 아닌 바로 위에서 실행한 테스트 네트워크로 붙어서 실행할 수 있게 해야하는데요. 방법은 터미널에서 Mist를 실행하면서 옵션으로 테스트 네트워크의 프로세스 정보와 커스텀 디렉토리를 넘기면 됩니다. 제가 실습하고 있는 환경인 Mac을 기준으로 터미널에서 다음과 같이 Mist를 실행하면 됩니다. ```sh /Applications/Ethereum\ Wallet.app/Contents/MacOS/Ethereum\ Wallet --rpc ./node1/geth.ipc --node-datadir ./node1 ``` \ 바로 위에서 실행한 `node1` 경로로 Mist를 실행시켰습니다. 잠시 후 다음과 같은 창이 뜨는 것을 볼 수 있습니다.  <br> 팝업 창 오른쪽 상단에 테스트 네트워크라는 표시가 `PRIVATE_NET`으로 되어 있는 것을 확인할 수 있습니다. 이제 `LAUNCH APPLICATION` 을 클릭하면 다음과 같이 콘솔에서 생성한 계정의 잔고를 Mist에서 확인할 수 있네요.  <br> ## explorer를 활용하여 블록 생성 이력 확인하기 한가지 더 나아가 볼까요. 매 블럭이 어떻게 생성되었는지 조금 더 편하게 확인할 수 있습니다. github에 공개된 [explorer](https://github.com/carsenk/explorer)라는 웹앱을 설치하면 되는데요. 우선 자바스크립트와 관려된 툴인 `npm`, `bower`만 설치되어 있으면 손쉽게 사용할 수 있습니다. github에 명시된대로 순차적으로 다음 명령을 실행하면 됩니다. ```shell $ git clone https://github.com/carsenk/explorer` $ cd explorer $ npm install $ bower install $ npm start ``` \ 앞서 `geth`를 실행할 때 `rpc` 설정으로 포트를 8545지정해 두었는데요. 사실 바로 이것 때문에 포트를 8545로 지정한 것입니다. `npm start`가 정상적으로 실행되었다면 노드 서버로 `explorer` 웹앱이 8000 포트로 실행된 것을 터미널에서 확인할 수 있습니다.  <br> 이제 8000포트로 브라우저에 접근해 봅시다. 다음과 같이 현재 테스트 네트워크의 블록 생성 정보를 쉽게 확인할 수 있는 웹앱이 보이면 정상입니다.  <br> ## 이더리움 보내기 이 글의 마지막 과업(?)이 남았습니다. 지금까지 마이닝의 베이스인 한 명의 계정만 있었는데 다른 사용자 계정으로 이더리움을 전송하는 것입니다. 이더리움을 전송하는 것은 `Mist`에서도 할 수 있지만 여기서는 콘솔로 실습해 보겠습니다. 콘솔이 띄워진 상태에서 `personal.newAccount()`로 이더리움을 전송받을 계정을 추가로 하나 더 만들어 둡니다. 그럼 이제 총 2명의 계정이 있겠지요? 이제 다음 명령을 순차적으로 입력합니다. ```shell > var u1 = eth.accounts[0] > var u2 = eth.accounts[1] > var amount = web3.toWei(10, "ether") > var tx = {from:u1, to:u2, value: amount} > personal.unlockAccount(u1) // 사용자 u1의 비번 입력 > eth.sendTransaction(tx) ``` \ 위 스크립트의 내용은 다음과 같습니다. 먼저 u1과 u2 변수에 각각 이더리움을 보낼 사람과 받을 사람의 주소를 담았습니다. `amount`에 10 이더를 선언한 후 이더리움 전송을 위한 `tx` 변수를 선언했습니다. 이 상태에서 `eth.sendTransaction(tx)` 를 호출하면 실패합니다. 보내는 계정이 잠겨있기 때문에 한번 풀어줘야 합니다. 따라서 `personal.unlockAccount(u1)` 을 호출하여 비번을 입력한 후 계정을 풀어야 트랜잭션을 등록할 수 있습니다. 정상적으로 성공했다면 받는 사람 계정에 10 이더가 전송되었겠지요? 만약 마이닝이 진행중이 아니라면 받는 사람 잔고에 10이더는 들어있지 않을 것입니다. 마이닝이 진행되고 해당 트랜잭션이 블록에 포함되었다면 콘솔에서 `u2` 의 잔고를 `eth.getBalance(u2)`를 호출하여 확인할 수 있습니다. 또는 `explorer`에서도 다음과 같이 확인할 수도 있습니다.  <br> ## 디버깅 잠깐 맛보며 마무리 오늘은 `geth`의 마이닝과정 코드를 살펴보기에 앞서 `Goland`의 디버깅 환경과 함께 실제 마이닝 작업을 로컬 테스트 네트워크로 실행해 봤습니다. 다음 글에서는 오늘 진행한 과정을 코드 레벨에서 살펴볼 예정입니다. 이제 오늘 셋팅한 디버깅환경을 맛을 보며 오늘 글을 마치고자 합니다. `geth` 소스 `miner` 폴더에 있는 `miner.go`파일을 열어 `self.worker.start()` 코드의 위치를 찾아보세요. 그리고 이 라인에 디버그 포인트를 찍은 후 콘솔에서 `miner.start()`를 실행해 봅시다. 아마 마이닝이 시작되기 전에 우리가 잡은 디버그 포인트에서 멈춘 후 다음과 같은 화면을 `Goland`에서 볼 수 있을 것입니다.  <br> IDE로 디버깅 포인트를 잡을 수 있으면 오늘 실습해 본 마이닝 과정을 진행해보면서 코드 흐름을 추적하기도 쉽습니다. 이제 다음 글에서 직접 코드를 살펴보도록 하겠습니다. 그럼 다음 연재에서 뵙겠습니다. ^^
👍 dreamya, mircokim22, ubg, megabyte77, frontalnh, wonsama, woojin.joe, kiwonhong, ingee, pangol, krwhale, ausfechten,