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

in kr •  7 months ago

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

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

  1. (본 글) Day 01: Geth 1.0 소스 받기 및 코드 분석을 위한 개발환경 셋팅(VS Code)
  2. Day 02: CLI 라이브러리 기반 geth의 전체 실행 구조
  3. Day 03: geth 노드의 실행 로직 분석 및 VS Code를 사용한 geth 디버깅
  4. 미정

Eng Version: Java developer's adventure to analyze go-ethereum(geth): Day 01

대상 독자

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

다루는 내용

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

geth란?

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

소스 받기

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

$ git clone https://github.com/ethereum/go-ethereum.git


만약 golang 개발환경이 구축되어 있다면 다음 명령을 사용하여 손쉽게 geth 소스를 받을 수 있습니다.

$ go get -d github.com/ethereum/go-ethereum


필요한 경우 geth 공식 매뉴얼 을 참조하여 소스를 빌드할 수 있습니다. 정상적으로 소스를 빌드하면 geth라는 실행가능한 바이너리를 갖게 됩니다.

Frontier로 전환

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

$ git checkout v1.0.0

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

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

VS Code는 다양한 종류의 확장 플러그인이 있는데요. 저는 평소에 Java로 개발할 때 인텔리제이를 사용합니다. 따라서 VS Code의 키바인딩을 인텔리제이로 변경해주는 JetBrains IDE Keymap를 설치해서 사용합니다. 필요하신 분들은 이 확장플러그인도 설치하시면 좋을 것 같습니다.

소스 읽기 출발점 main

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

package main

import (
"fmt"
)

func main() {
fmt.Println("Hello, Golang and Geth")
}


golang은 함수의 선언을 위해 func 키워드가 예약되어 있습니다. func 키워드에 이어 함수의 이름과 바디를 작성하면 golang에서의 함수가 됩니다. golang에서는 웹에서 실행할 수 있는 환경을 지원합니다. 만약 위 코드를 실행해 보고 싶다면 다음 url에 들어가면 확인해 볼 수 있습니다.

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

d01_func_search.png

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

d01_geth_main.png

main 함수 분석

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

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 CodeGo to Definition 단축키를 사용하여 app 인스턴스의 선언부를 찾아낼 수 있습니다. 선언부는 같은 파일인 main.go 의 58번 라인에 있습니다.

var (
    gitCommit       string // set via linker flag
    nodeNameVersion string
    app             *cli.App
)


다음으로 역시 우리는 Find All Reference 단축키를 사용하여 app 인스턴스의 초기화 로직을 찾아낼 수 있습니다. 다음은 app 인스턴스를 참조하는 위치를 VS Code가 보여주는 화면입니다.

d01_find_ref.png

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

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의 자세한 스펙은 다음 공식 매뉴얼을 참고하여 주세요.

결론

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

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

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

계속 기대합니다.^^

·

네 차근히 한 꼭지씩 올려보겠습니다. :)

고맙게 잘 보고 있습니다. ^^ 계속 TBD 기대합니다.

·

감사합니다. TBD 딱지를 떼고 하나씩 주제를 늘려가 보겠습니다. :)