
저번 포스팅에 이어서 작업 증명(Proof of Work) 알고리즘을 만들어 보도록 하겠다.
작업 증명에 대한 이해가 필요하다면 아래링크 참고
블록체인 이해하기 : 그래픽카드 가격은 왜 올라가는 것일까?
암호화폐 채굴로 인하여 그래픽 카드 가격이 많이 올랐다는 뉴스를 본 적이 있을 것입니다. 그래픽카드 대란, 채굴 전용카드로 막을 수 있을까 게임이나 콘텐츠 제작에 쓰여야 하는 고성능 그래
heun.tistory.com
환경설정, 기본적인 소스 구조는 이전에 작성했던 포스팅을 참고
위 링크를 보면 작업 증명에 대해 간단하게 설명되는데 추가로 설명하자면 작업 증명은 채굴을 하기 위한 알고리즘이라고 생각하면 된다. 그렇다면 암호화폐 채굴이라는 건 무엇을 하는 것일까? 비트코인의 경우 비트코인을 원하는 참가자들이 작업을 통해 비트코인이 너무 빠르게 지급되지 않도록 몇 가지 방법을 찾아야 했는데, 비트코인은 참가자들이 문자와 숫자의 조합을 통해 특정 숫자가 선행에 0이 포함될 때까지 해시를 하게 함으로써 해결하였다.
정리해 보자면 채굴은 3가지 항목 1. 노드(참가자), 2. 비트코인(보상), 3. 방법(작업 증명방식)으로 설명이 가능하다. 노드가 작업 증명을 방식을 통해 문제를 해결하면 새로운 블록을 등록하게 되고 그에 따른 비트코인이란 보상을 얻게 된다. 이 과정을 채굴이라고 한다. 작업 증명 알고리즘 코딩을 시작해 보도록 하겠다.
블록체인 만들면서 Go언어 공부하기 #3
// 해시 값 선행에 있는 0의 갯수
const difficulty = 1
// 블록 정보
type Block struct {
Index int
Timestamp string
BPM int
Hash string
PrevHash string
Difficulty int
Nonce string
}
// 블록을 모두 연결한 변수
var Blockchain []Block
// POST request를 보내 새로운 블록을 생성
type Message struct {
BPM int
}
// 블록 중복 생성 방지
var mutex = &sync.Mutex{}
const difficulty는 해시 값에 선행되어 있는 0의 개수를 말하고 difficulty가 높아질수록 0의 개수가 많아지니 난이도가 그만큼 올라가고 찾는데 힘들어질 것이다. Message는 REST API를 이용하여 POST request를 보내 새로운 블록을 생성하는 데 사용될 예정이다.
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()
// 블록 중복 생성 방지
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)
}
handlewriteblock함수에는 mutex.lock()과 mute.unlock()을 작성해야 한다. 블록 중복 생성 방지 역할을 해준다.
작업 증명(Proof of Work)
작업 증명으로 생성된 해시는 선행에 있는 0의 개수와 반드시 일치해야 한다. 0의 개수는 difficulty(난이도, 블록헤더에 담겨 있는 값)에 의해서 결정되고 difficulty를 증가시킴으로써 작업증명을 어렵게 할 수 있다.
func isHashValid(hash string, difficulty int) bool {
prefix := strings.Repeat("0", difficulty)
return strings.HasPrefix(hash, prefix)
}
prefix 변수 선언하고 difficuty에 정의된 수만큼 0을 리턴 받는다. 0의 개수가 일치하면 True를 return 해준다.
전에 포스팅에 generateBlock 함수에서 difficulty가 추가된다. 논스의 개념을 아직 자세하게 설명하지 않았지만 difficulty로 hash선행에 있는 0의 개수를 설정해 준다면 논스는 원하는 0의 개수와 일치할 때까지 논스의 크기를 키워가면서 계속 해시화 하게 되는데 이 작업증명을 할 때까지의 횟수를 논스라고 한다. generateBlock함수에서 i를 16진수화 한 이후에 논스에 대입한다.
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.Hash = calculateHash(newBlock)
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
}
테스트를 진행.



작업증명이 완료된 work done! 의 해시값을 살펴보게 되면 맨 앞에 0이 하나 있는 것을 볼 수 있다. difficulty를 1로 설정했기 때문에 0의 개수가 1개인 해시값을 찾아낸 걸 볼 수 있다.
Reference
GitHub - heun630/Blockchain-ProoOfWork-basic
Contribute to heun630/Blockchain-ProoOfWork-basic development by creating an account on GitHub.
github.com
'Develop > Go' 카테고리의 다른 글
| [Go] go-ethereum 분석하기: blockchain_insert.go의 report 함수 (0) | 2023.05.19 |
|---|---|
| [Go] 블록체인 만들면서 Go언어 공부하기 #2 (0) | 2022.01.04 |
| [Go] 블록체인 만들면서 Go언어 공부하기 #1 (0) | 2022.01.03 |