[블록체인 개발 공부] 블록체인 개발하기 PART 3 : 채굴 알고리즘 (Proof of Work : POW)

in #kr7 years ago (edited)

CoralHealth 블로그의 https://medium.com/@mycoralhealth/code-your-own-blockchain-mining-algorithm-in-go-82c6a71aba1f 내용을 번역한 글입니다.

시작 하기전에 이전 포스팅을 보시지 못한 분들은 아래 링크를 통해 확인하시고
이번 포스팅을 보시는것을 추천 드립니다.
part1 기본적인 블록체인 서버 개발
part2 블록체인 네트워크 동작 개발

최근 비트코인, 이더리움 등 채굴 유행 때문에 많은 사람들이 채굴이 무엇인지 궁금할 것이라고 생각됩니다.

암호화폐를 채굴하기 위해서 창고안에 많은 GPU(그래픽 카드)를 놓고 한달에 몇 천 만원씩 돈을 벌었다는 이야기도 들었을 것입니다.
도대체 암호화폐 채굴이 무엇인지?
어떻게 동작하는건지?
어떻게 채굴 알고리즘을 코딩 할 수 있는지?

궁금 할 것이라고 생각됩니다.
이번 포스팅에서 하나씩 알아봅시다. 그리고 채굴 알고리즘을 코딩해 봅시다.
오늘 알아볼 채굴 알고리즘은 Proof of Work(POW 작업증명방식)입니다.
이 방식은 현재 비트코인, 이더리움 등에서 사용되는 방식입니다.
다음 포스팅에서는 Proof of Stake(POS 지분증명방식)알아 보겠습니다.

암호화폐 채굴이란 무언인가?

암호화폐가 가치를 가지기 위해서는 수요량 보다 공급량이 적어야합니다. 만약에 어떤 한 사람이 비트코인을 원할 때 마다 제공한다면 비트코인은 쓸모 없는 화폐가 될 것입니다.
비트코인 알고리즘은 매 10분마다 네트워크 상에 채굴에 승리한 사람에게 지급 됩니다. 비트코인이 전부 발행 되려면 약 122년 정도 걸리며 지급되는 양은 시간이 지날수록 감소합니다.

비트코인이 지급 되는 프로세스는 몇 가지 작업이라는 것을 완료해야 합니다. 이 작업은 다른 사람들과 경쟁을 하게 되며 승리자에게 비트코인이 주어집니다. 이 프로세스는 광산에서 금을 채굴하는 과정과 유사하다고 하여 채굴이라고 합니다.

비트코인 알고리즘은 참여자(노드)들이 경쟁을 하게 하여 지급되는 속도를 늦추게 합니다.

채굴은 무엇을 하는 것인가?

아마도 비트코인 채굴이 어떤 작업을 하는 것인지 검색 해보면 비트코인이 노드(채굴하는 사람의 컴퓨터)에게 어려운 수학문제를 낸다고 나올 것입니다. 기술적으로는 사실이지만 단순히 수학 문제라고 하기에는 어렵습니다. 채굴이 어떻게 동작하는지 알기 위해서는 암호화해시를 이해해야 합니다.

암호화 해시란 무엇인가?


단방향 암호화는 “Hello world”라는 값을 입력할 경우 해독 할 수 없는 문자를 출력합니다.
이러한 함수들은 다양한 특성을 가지며 복잡합니다. 더 복잡한 알고리즘은 역 공학자(암호화 된 것을 원본으로 분석하는 사람 또는 해커)들을 힘들게 합니다.
따라서 암호화 알고리즘은 사용자 비밀번호나 군사코드를 보호하는데 강력합니다.

가장 많이 사용하는 암호화 알고리즘 SHA-256를 이용하여 알아봅시다.
이 웹사이트에서 쉽게 SHA-256 해시 값을 계산할 수 있습니다.
“Hello world”라고 입력할 경우 어떤 결과가 나오는 지 봅시다.

1__qWZ8MB6pKezY_76qPjEjA.png
계속해서 “Hello world”를 해시화 해봅시다.
아마도 계속 같은 값이 나올 것입니다. 이를 멱등성이라고 합니다.

암호화 알고리즘의 근본적인 속성은 역 공학자(암호화 된것을 원래의 값을 추적하는 사람 또는 해커)들이 쉽게 해시값을 보고 어떤 값이 해시화 된 건지 찾지 못하게 하는 것입니다.
예를 들어 위에서 SHA-256 해시를 이용하여 “Hello world”를 계속해서 해시화된 결과를 보면 똑 같은 값이 나올 것입니다.
하지만 그 해시 값을 보고 “Hello world”라는 값을 얻기는 매우 힘듭니다.
이것을 암호화 기법 중 단방향 암호화라고 합니다.

비트코인은 Double SHA-256을 사용합니다.
이는 “Hello world”를 SHA-256으로 암호화하고 한번 더 해주는 방법입니다.

채굴


이제 암호화에 대해서 이해 했으니 암호화폐의 채굴에 대해서 알아 봅시다.
비트코인은 비트코인을 받기 원하는 참가자들이 작업을 통해 비트코인이 너무 빠르게 지급되지 않도록 몇가지 방법을 찾아야 했습니다. 비트코인은 참가자들이 문자와 숫자의 조합을 통해 특정 숫자의 선행에 0이 포함될 때까지 해시를 하게 함으로써 해결하였습니다.
예를들어 조금전 해시화 해주는 사이트에 들어가서 886을 해시하면 ‘0’ 3개가 앞에 붙게 됩니다.

1_5l3FgMIR5Gn_AUZ1X5mW9Q.png

하지만 886의 해시 값이 3개의 0이 존재한다는 것 어떻게 알까요?
이것이 포인트입니다. 많은 숫자와 문자 조합하여 테스트한 결과 0이 3개인 값을 얻을 수 있었습니다.
누구나 쉽게 많은 조합을 통해 886이 선행에 0이 3개가 붙는걸 찾아 낼 수 있습니다.
이를 토대로 많은 참가자들 중에 결과를 먼저 찾는 사람이 자신이 작업을 완료했다는 것을 주장하고 작업을 증명합니다.
승리자는 이를 보상으로 비트코인을 얻게 될 것 입니다.
이러한 방법을 Proof of Work(작업증명) 이라고 합니다.

비트코인은 이것보다 더 복잡합니다. 그리고 동적으로 난이도를 쉽거나 어렵게 할 수도 있습니다.
비트코인은 10분마다 발행하는 것을 목표로 하는데 만약에 많은 사람들이 채굴을 한다면 보상을 받기 어렵게 해야합니다.
이것을 난이도 조정이라고 합니다.

이제 코딩을 시작해봅시다.


이제 우리는 개발에 필요한 사전 지식을 숙지 했습니다.
이제 Proof of Work (작업증명방식) 알고리즘을 만들어 봅시다.
이전 포스팅에서 블록체인 서버와 네트워킹에 대해서 공부했었습니다.
아직 못 보신 분들이 있다면 이 링크를 통해 보시기 바랍니다.
그리고 이미 이 내용을 알고 있다면 바로 작업증명방식에 대해서 알아 봅시다.

1_z0fgOU0iYm7Pjc5Zn5nCjA.png

첫번째 포스팅에서 블록체인을 위한 GO서버를 만들었었습니다. main.go파일에 코드를 작성했었습니다.
이 파일은 REST API와 우리가 필요로 하는 모든 기능을 가진 블록체인 코드가 될 것입니다.
그리고 Postman을 이용하여 POST와 GET Request 만을 가지고 새로운 블록을 만들고 브라우저를 통해 블록을 볼 수 있을 것입니다.

Imports

개발을 위해서는 아래 패키지가 필요합니다.
이전에 했던 분은 그대로 이용하면 되지만 없는 분들은 아래 명령어를 통해 패키지를 설치하세요.
go get github.com/davecgh/go-spew/spew
go get github.com/gorilla/mux
go get github.com/joho/godotenv
그리고 .env 파일을 루트 디렉토리에 생성하고 ADDR=8080이라고 입력하세요.

다음 진행에 앞서 블록체인에서 이전 블록의 해시와 현재 블록의 Prehash 비교를 통해
검증하여 블록체인의 무결성을 보장되는지를 이전 포스팅을 통해 알 수 있습니다.
못 보신 분들은 여기서 확인 하시기 바랍니다. BPM은 우리가 블록에 저장할 내용입니다.

몇가지 수정 사항

main.go 파일에 블록 데이터 구조체에 새로운 변수를 선언해 봅시다. (main.go 파일은 첫번째 포스팅에서 사용한 파일입니다.

const difficulty = 1
    

    type Block struct {
            Index      int
            Timestamp  string
            BPM        int
            Hash       string
            PrevHash   string
            Difficulty int
            Nonce      string
    }
    

    var Blockchain []Block
    

    type Message struct {
            BPM int
    }
    

    var mutex = &sync.Mutex{}

Difficulty는 해시 값 선행에 있는 0의 갯수를 의미합니다. 0갯수가 많아 진다면 찾는데 힘들 질 것입니다.
그래서 이 값은 1로 시작 하겠습니다. Block은 데이터를 가진 각각의 블록을 나타내는 변수입니다.
Nonce에 대해서는 곧 설명 드리겠습니다. Blockchain은 블록을 모두 연결한 변수입니다. Message는 REST API를 이용하여 POST request 보내 새로운 블록을 생성하는데 사용될 예정입니다. Mutex는 동시에 블록이 생성하는 것을 막기 위해 사용됩니다.

HandleWriteBlock 함수에는 mutex lockunlock을 작성해야합니다. 동시에 블록이 생성되는것을 막기 위해서 입니다.

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Content-Type", "application/json")
            var m Message
    

            decoder := json.NewDecoder(r.Body)
            if err := decoder.Decode(&m); err != nil {
                    respondWithJSON(w, r, http.StatusBadRequest, r.Body)
                    return
            }   
            defer r.Body.Close()
    

            //ensure atomicity when creating new block
            mutex.Lock()
            newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
            mutex.Unlock()
    

            if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
                    Blockchain = append(Blockchain, newBlock)
                    spew.Dump(Blockchain)
            }   
    

            respondWithJSON(w, r, http.StatusCreated, newBlock)
    

    }

Proof of Work 작업증명

이제 채굴(작업증명)에 대해서 알아 봅시다. 작업증명은 블록체인에 새로운 블록이 추가 되기 전에 완료 되어야 합니다. 작업증명을 하는동안 생성된 해시가 요구사항을 충족하는지 확인하는 간단한 함수 예제로 시작해봅시다.

요구사항은 다음과 같습니다.

  • 작업증명을 통해 생성된 해시는 선행에 0의 갯수가 반드시 일치해야 합니다.
  • 0의 갯수는 difficulty의 값으로 결정됩니다.
  • Difficulty의 값을 증가시킴으로써 작업증명을 어렵게 할 수 있습니다.

IsHashValid 함수를 작성해 봅시다.

func isHashValid(hash string, difficulty int) bool {
            prefix := strings.Repeat("0", difficulty)
            return strings.HasPrefix(hash, prefix)
    }

GO 언어에서는 string 패키지에 Repeat과 HasPrefix 함수를 제공합니다.
Prefix라는 변수를 선언하고 difficulty의 정의된 수만큼 0을 리턴 받습니다.
그리고 0의 갯수가 일치하면 True리턴하고 아니면. False를 리턴합니다.

이제 generateBlock함수를 만들어 봅시다.

func generateBlock(oldBlock Block, BPM int) Block {
        var newBlock Block

        t := time.Now()

        newBlock.Index = oldBlock.Index + 1
        newBlock.Timestamp = t.String()
        newBlock.BPM = BPM
        newBlock.PrevHash = oldBlock.Hash
        newBlock.Difficulty = difficulty

        for i := 0; ; i++ {
                hex := fmt.Sprintf("%x", i)
                newBlock.Nonce = hex
                if !isHashValid(calculateHash(newBlock), newBlock.Difficulty) {
                        fmt.Println(calculateHash(newBlock), " do more work!")
                        time.Sleep(time.Second)
                        continue
                } else {
                        fmt.Println(calculateHash(newBlock), " work done!")
                        newBlock.Hash = calculateHash(newBlock)
                        break
                }

        }
        return newBlock
}

Part1 포스팅에서 했던것 과 동일하지만 Difficulty가 추가 됩니다.
이는 앞에 말씀드렸듯 hash선행문자에 0의 갯수를 나타냅니다.
For문은 이 함수에 치명적인 부분입니다. 반복문 안에서 i의 값을 16진수로 바꾸고 Nonce에 대입할 것입니다. 그리고 원하는 0의 갯수와 일치할때까지 계속 해시화를 할것입니다. 일치할때까지 찾아 내는 횟수를 Nonce라고 합니다. 그리고 작업증명을 하는데 시간이 걸리는 걸 확인하기 위해서 1초 sleep을 합니다.

완성된 코드는 저의 github에서 보실수 있습니다. https://github.com/evasioner/blockchain

테스트 해봅시다

먼저 go run main.go 명령어로 실행합시다

그리고 아래 그림처럼 POSTMAN을 이용해서 POST request를 보냅니다.
스크린샷 2018-04-05 오전 12.41.48.png

보내고 나면 아래 그림처럼 콘솔에 작업증명을 하는것을 보실 수 있습니다.

스크린샷 2018-04-05 오전 12.42.06.png

작업 증명이 끝나면 블록이 생성됩니다.

이것은 브라우저에서도 볼수 있습니다.
스크린샷 2018-04-05 오전 12.42.31.png

NEXT STEP


다음 시간에는 지분증명방식 Proof of Stake (POS) 작동 방식을 알아보고
어떻게 코딩하면 되는지 알아 봅시다.

감사합니다.

Sort:  

저에게는 좀 어려운 내용이지만 좋은 글이네요.
잘보고 갑니다.

역시 좋은 글 이군여

Congratulations @evasioner! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

You got your First payout

Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

Upvote this notification to help all Steemit users. Learn why here!

Do not miss the last announcement from @steemitboard!