자바 개발자의 go-ethereum(geth) 소스 분석기: Day 01

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@woojin.joe·
0.000 HBD
자바 개발자의 go-ethereum(geth) 소스 분석기: Day 01
# 자바 개발자의 go-ethereum(geth) 소스 분석기: Day 01

이 글은 자바 개발자의 go-ethereum(geth) 소스 분석기 시리즈의 연재 중 첫 번째 글입니다. 앞으로 다음과 같은 내용으로 연재를 계획하고 있습니다.

1. **(본 글)** Day 01: Geth 1.0 소스 받기 및 코드 분석을 위한 개발환경 셋팅(VS Code)
2. [Day 02: CLI 라이브러리 기반  `geth`의 전체 실행 구조](https://steemit.com/kr/@woojin.joe/go-ethereum-geth-day-02)
3. Day 03:  `geth` 노드의 실행 로직 분석 및 VS Code를 사용한 `geth` 디버깅
4.  미정


> Eng Version: [Java developer's adventure to analyze go-ethereum(geth): Day 01](https://steemit.com/ethereum/@woojin.joe/java-developer-s-analysis-of-go-ethereum-geth-day-01)

## 대상 독자

이 연재는 먼저 독자 분들이 적어도 Java와 같은 OOP 계열의 언어로 프로그래밍 경험이 있다는 것으 가정합니다. 또한 계정, 채굴 등 블록체인과 이더리움과 관련된 기초적인 개념을 알고 있다고 가정합니다.



## 다루는 내용

이 글에서는 `geth` 소스의 다운로드 및 설치와 `geth` 실행의 최초 진입점이 되는 `main` 함수 구성을 가볍게 살펴봅니다. 또한 코드를 읽으면서 보게 될 Golang만의 구문인 `func` 와 패키지 초기화 내용을 살펴봅니다.




## geth란?

[Geth](https://geth.ethereum.org) 는 `golang`으로 구현한 이더리움 클라이언트 중 하나입니다.  `geth`는 이더리움 프로토콜의 공식 구현체로 충실하게 스펙을 구현하고 있습니다. 따라서 `geth`의 내부를 읽고 이해하는 것은 이더리움 프로토콜을 정확하게 이해하는데 큰 도움이 됩니다.



## 소스 받기

`geth` 소스 분석을 위한 첫번째 단계는 소스를 받는 것입니다. 소스를 로컬에 받는 방법은 두가지가 있습니다. 첫번째는 `git` 을 사용하여 소스를 클론하는 것입니다. 다른 한가지 방법은 `golang`에서 지원하는 패키지 관리 기능을 사용하여 소스를 받는 것입니다. 먼저 첫번째 방법으로 `git`을 사용하면 다음과 같은 명령으로 소스를 로컬에 받을 수 있습니다.


```sh
$ git clone https://github.com/ethereum/go-ethereum.git
```
\
만약 `golang` 개발환경이 구축되어 있다면 다음 명령을 사용하여 손쉽게 `geth` 소스를 받을 수 있습니다. 



```sh
$ go get -d github.com/ethereum/go-ethereum
```
\
필요한 경우 `geth` [공식 매뉴얼](https://geth.ethereum.org/install) 을 참조하여 소스를 빌드할 수 있습니다. 정상적으로 소스를 빌드하면 `geth`라는 실행가능한 바이너리를 갖게 됩니다.

### Frontier로 전환

때때로 오픈소스 코드를 분석하기 위해서 초기 버전으로 돌아갈 필요가 있습니다. 이는 최신버전의 코드보다 초기 버전의 코드가 상대적으로 구현이 간단하기 때문입니다. 따라서 여기서 우리는 `geth` 의 최초 공식버전인 1.0.0 ` Frontier`를 사용하려고 합니다. `Frontier`는 1.0.0 버전의 명칭입니다. Frontier 버전의 코드는 `git`에  [v1.0.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.0.0)으로 태깅되어 있으므로 다음 명령을 사용히여 손쉽게 해당 버전의 코드로 전환할 수 있습니다.



```sh
$ git checkout v1.0.0
```

 


## 코드 리딩을 위한 환경 도구 셋팅

`geth` 소스를 분석하기 위해서 우리는 `golang`을 지원하는 IDE나 에디터를 사용해야 합니다. 이미 `golang`을 지원하는 다양한 에디터가 있는데요. 이 연재에서 저는 `VS Code`를 사용할 예정입니다. 만약 `VS Code`로 `golang`의 소스 읽기를 함께 하길 원하시는 분은 다음 가이드라인에 따라 `go` 확장을 설치하시면 됩니다.

- https://code.visualstudio.com/docs/languages/go

> VS Code는 다양한 종류의 확장 플러그인이 있는데요. 저는 평소에 Java로 개발할 때 인텔리제이를 사용합니다. 따라서 VS Code의 키바인딩을 인텔리제이로 변경해주는  [JetBrains IDE Keymap](https://marketplace.visualstudio.com/items?itemName=isudox.vscode-jetbrains-keybindings)를 설치해서 사용합니다. 필요하신 분들은 이 확장플러그인도 설치하시면 좋을 것 같습니다.




## 소스 읽기 출발점 main
모든 소프트웨어는 반드시 실행하기 위한 진입점 `main` 함수를 갖습니다.  소스를 분석하기에 앞서 `golang` 에서는 함수를 어떻게 표현하는지를 확인해 봐야 할 것 같습니다. 

  ```go
package main

import (
 "fmt"
)

func main() {
 fmt.Println("Hello, Golang and Geth")
}
  ```
\
`golang`은 함수의 선언을 위해 `func` 키워드가 예약되어 있습니다. `func` 키워드에 이어 함수의 이름과 바디를 작성하면 `golang`에서의 함수가 됩니다. `golang`에서는 웹에서 실행할 수 있는 환경을 지원합니다.  만약 위 코드를 실행해 보고 싶다면 다음 url에 들어가면 확인해 볼 수 있습니다.

* https://play.golang.org/p/70Txg8kmIKi



`golang`의 함수 표현법을 확인했으니 이제 우리는 `geth  ` 의 `main ` 함수를 찾을 수 있습니다. 그런데 한가지 문제가 있습니다. `geth` 에는 다양한  `func main` 이 있습니다. 바로 이렇게요.


![d01_func_search.png](https://steemitimages.com/DQmQidvgMUH5HaiSfb3mvmU9bePGNSkV2Xm78VbCKWxT8i5/d01_func_search.png)



다행히 우리에게는 `geth`의 `main` 함수를 찾을 큰 힌트가 있습니다. 그건 바로 `geth`를 실행하기 위한 바이너리인 `geth` 키워드입니다. `geth` 클라이언트를 실행하려면 우리는 반드시 터미널에서 `geth` 명령을 사용해야 합니다. 유추한대로 다음 스크린샷처럼  `cmd/geth` 폴더에 최초 진입점인  `main` 함수를 찾았습니다.



![d01_geth_main.png](https://steemitimages.com/DQmUp49Dmv2JSQED6PCVtBoCcWb5u1737GcGMF4DctpPUqf/d01_geth_main.png)


## main 함수 분석

 `main` 함수의 코드는 다음과 같습니다.

```go
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	defer logger.Flush()
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
```
\
처음 2라인의 코드는 주로 실행환경변수와 로깅 관련된 코드로 보이므로 일단 넘어가도 될 것 같습니다. 이 코드에서 가장 중요한 부분은 4번째 라인의  `app.Run(os.Args)` 입니다. 코드를 보면 `main` 함수의 역할은  `app.Run()` 을 호출하는 것이 핵심임을 알 수 있습니다.



그럼 이제 우리는 다음과 같은 질문을 던질 수 있습니다.

> * `app` 인스턴스는 어디에 선언되어 있는 거지?
> * `app` 인스턴스의 초기화 로직은 어디에 있는 거지?



우리는  `VS Code ` 의 `Go to Definition` 단축키를 사용하여  `app` 인스턴스의 선언부를 찾아낼 수 있습니다.  선언부는 같은 파일인  `main.go`  의 58번 라인에 있습니다.

```go
var (
	gitCommit       string // set via linker flag
	nodeNameVersion string
	app             *cli.App
)
```
\
다음으로 역시 우리는 `Find All Reference` 단축키를 사용하여 `app` 인스턴스의 초기화 로직을 찾아낼 수 있습니다.  다음은  `app` 인스턴스를 참조하는 위치를  `VS Code`가 보여주는 화면입니다.



![d01_find_ref.png](https://steemitimages.com/DQme2EC3gA5svNy9vGDm6hnHunKh19JjgAVRBDYjrjBaMmy/d01_find_ref.png)



검색 결과에서 2번째에 `app`의 초기화 로직이 보이는군요.  이 로직은 `init` 함수 안에 있습니다. 다음은 `init` 함수의 앞부분 코드 일부입니다.



```Go
func init() {
	if gitCommit == "" {
		nodeNameVersion = Version
	} else {
		nodeNameVersion = Version + "-" + gitCommit[:8]
	}

	app = utils.NewApp(Version, "the go-ethereum command line interface")
    app.Action = run
	app.HideVersion = true // we have a command to print the version
	app.Commands = []cli.Command{
    ...
```
\
이 코드를 보면 우리는 `app` 인스턴스의 초기화는 `utils.NewApp` 실행결과라는 것을 알 수 있습니다. 그럼 `init` 함수는 누가 호출하고 있는 걸까요? 그것은 `golang`의 역할로 스펙을 따릅니다.



### Package Initialization

이 코드가 어떻게 동작하는지 이해하기 위해 `golang` 관련 로직을 조사하다 보니 `golang` Package Initialization라는 개념을 알 수 있었습니다.   `golang`  에서 사용자는 패키지 단위의 스코프를 갖는 변수를 선언하고 초기화 할 수 있습니다.  위 두 코드에서 살펴본  `app` 인스턴스의 선언과 초기화를 담당하는 `init` 함수가 바로  `golang`의 스펙이었습니다. 요약하면 최초 실행될 때 패키지가 로드되는 시점에 `app` 인스턴스의 선언/초기화가 이뤄진다고 볼 수 있습니다. Package Initialization의 자세한 스펙은 다음 공식 매뉴얼을 참고하여 주세요.

* https://golang.org/ref/spec#Package_initialization




## 결론

오늘은 가볍게 `geth` 소스 분석을 위한 개발환경 셋팅과 최초 실행 진입점이 되는 `main` 함수를 분석했습니다. `main` 함수에서 가장 중요한 부분은 `app` 인스턴스이며 이는  `utils.NewApp()`의 실행결과 라는 것을 확인했습니다. 다음 글에서 우리는  `utils.NewApp()` 어떻게 동작하는지 알아볼 예정입니다. 



그럼 다음 연재에서 뵙겠습니다.
👍 , , , , , , , , , ,