이번엔 작업증명(Proof of Work)을 추가해보겠다. 현 상황에서는 블록을 추가할 때 아무런 과정없이 그냥 추가할 수 있지만, 실제 블록체인에서는 합의 알고리즘을 통해 블록을 생성한다. 합의 알고리즘은 암호 화폐 네트워크의 무결성과 보안을 유지하기 위해 중요하다. 합의 알고리즘은 분산화된 각각의 노드들이 서로 누가 진짜인지 합의할 수 있게 한다.
작업 증명은 새로운 블록을 블록체인에 추가하는 ‘작업’을 완료했음을 ‘증명’하는 것이라고 이해하면 된다. 새로운 블록을 블록체인에 추가하려면, 그 새로운 블록의 블록 해쉬를 계산해내야하고, 그 블록 해쉬를 계산해내려면 그 블록의 블록 헤더 정보 중의 하나인 nonce값을 계산을 통해 구해야 한다.
Nonce : 최초 0에서 시작하여 조건을 만족하는 해쉬값을 찾아낼때까지의 1씩 증가하는 계산 횟수
nonce 값을 입력값 중의 하나로 하여 계산되는 블록 해쉬값이 특정 숫자보다 작아지게 하는 과정이 곧 채굴인 것이다.
여기서 특정 숫자가 바로 블록 해시를 생성할 때 사용하는 SHA256 알고리즘의 결과값인 256 bit에서
target bit 값만큼을 shift 연산자를 통해 왼쪽으로 비트를 밀어주어 획득하는 값이다.
코드와 함께 살펴보겠다.
우선 새로운 nonce가 block 구조체에 추가되었음을 알린다.
type Block struct {
TimeStamp int32 `validate:"required"`
Hash []byte `validate:"required"`
PrevHash []byte `validate:"required"`
Data []byte `validate:"required"`
Nonce int `validate:"min=0"`
}
그리고 이런 구조를 선언하고 설계할 때는 bitcoin의 구조를 참고했다.(TMI)
https://www.linkedin.com/pulse/blockchain-data-structure-ronald-chan
Type ProofOfWork
type ProofOfWork struct {
block *Block
target *big.Int
}
Block과 Target 값을 가진 구조체를 선언해주었다.
Function NewProofOfWork
const targetBits = 6
테스트 단계에 있기 때문에 채굴 난이도는 6정도로 설정해주었다.
// Build a new ProofOfWork and return
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) // target = 1 << 256-targetBits
pow := &ProofOfWork{b, target}
return pow
}
target = 1 << 256-targetBits 를 통해 타켓 값을 얻어내고, ProofOfWork 구조체에 담아준다.
function prepareData
Nonce 라는 것은 단순 Counter 이며 이는 블록 헤더와 결합되어 target 보다 더 작은 값을 찾기위해 데이터를 준비시키기 위한 메서드라고 볼 수 있다. 기존의 블록 헤더에 있던 값들과 난이도를 포함하여 nonce 를 결합한 데이터를 준비한다.
위에서 언급했듯이 shift연산을 통해 얻어낸 타겟 값보다 작은 값이 나올 때까지 nonce값을 계속 1씩 증가시키며 계산하는데, 그 과정에서 해시를 계산할 데이터를 준비해주는 함수이다.
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
[]byte(pow.block.PrevHash),
[]byte(pow.block.Data),
IntToHex(int64(pow.block.TimeStamp)),
IntToHex(int64(targetBits)),
IntToHex(int64(nonce)),
},
[]byte{},
)
return data
}
Function Run
반복문을 통해 계산을 진행하며 target과 해시된 data를 비교하여 채굴을 진행하는 함수이다.
여기서 반복문은 무한 루프이다. 타겟보다 계산된 해시값이 더 작을 때 작업을 종료한다.
// Mining
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
for nonce < maxNonce {
data := pow.prepareData(nonce)
hash = sha256.Sum256(data)
fmt.Printf("\r%x", hash)
hashInt.SetBytes(hash[:])
if hashInt.Cmp(pow.target) == -1 {
break
} else {
nonce++
}
}
return nonce, hash[:]
}
Function Validate
특정 블록이 작업증명을 거친 것인지 확인하기 위한 함수이다.
// Validate hash
func (pow *ProofOfWork) Validate() bool {
var hashInt big.Int
hash := sha256.Sum256(
pow.prepareData(pow.block.Nonce),
)
hashInt.SetBytes(hash[:])
isValid := hashInt.Cmp(pow.target) == -1
return isValid
}
블록들을 보여줄 때 해당 블록을 증명해줄 것이다.
// Show Blockchains
func (bc Blockchain) ShowBlocks() {
for _, block := range GetBlockchain().blocks {
pow := NewProofOfWork(block)
fmt.Println("TimeStamp:", block.TimeStamp)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Hash: %x\n", block.Hash)
fmt.Printf("Prev Hash: %x\n", block.PrevHash)
fmt.Printf("Nonce: %d\n", block.Nonce)
fmt.Printf("is Validated: %s\n", strconv.FormatBool(pow.Validate()))
}
}
fmt.Printf("is Validated: %s\n", strconv.FormatBool(pow.Validate())) // Here^^
이제 새로 추가한 코드들을 기존 코드에 적용하도록 하겠다.
새로운 블록을 생성할 때 이제는 바로 해시를 계산하는게 아니라
새로운 ProofOfWork를 생성한 후 채굴을 진행한다.
그래서 newblock.calculateHash() 라인을 pow := NewProofOfWork(block)로 바꿔주었다.
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{prevBlockHash, []byte{}, time.Now().Unix(), []byte(data), 0}
pow := NewProofOfWork(block)
block.Nonce, block.Hash = pow.Run()
return block
}
package main
import "strconv"
func main() {
chain := GetBlockchain()
for i := 1; i < 10; i++ {
chain.AddBlock(strconv.Itoa(i))
}
chain.ShowBlocks()
}
블록 하나하나가 추가될 때마다 Added를 출력하며,
마지막으로 블록 전체를 출력해주도록 하였다.
실행 시 모습 :
0166be9d913999feea4fc94b818202d6d2b12484c201a6f223b311fcc6b385fd Added
0215c646e6a3faac0f954e8bf88263816aefefdbeeb9363f6c7b8b388b2303a6 Added
00b2aac6a4f0130af48d941ef153cf5c4c64d7a17f9b71ba17d2f1995d55cc33 Added
02913ec4213f7329574ce2a8cc25b0a0e92b00a40e35403cc48a68ffe21075a8 Added
026d60bcf8b6ff1652e2c065c593162e109070b91fba291d0e3b5e3261e281fd Added
00f04419689bcefb43fe386381f81dcf86e5c72a91c43444e015f22863ad1e39 Added
005c4a22a662cc653f6d60bd19eeb6f243705298e76bc485fa22d91c7edb2283 Added
020868cc9190e17d1b9a9e05f27ba4358d5a6c4a0491446ba45628ccfbd3239b Added
01cebbd78196c674e64a50854b1ec9be7da8c0804e47d43721fee51589e5a81d Added
03a312dd6b769a858733a0f3131b02084e90c30d03b338f1ea529734e3703245 Added
TimeStamp: 1642923947
Data: Genesis Block
Hash: 0166be9d913999feea4fc94b818202d6d2b12484c201a6f223b311fcc6b385fd
Prev Hash:
Nonce: 47
is Validated: true
TimeStamp: 1642923947
Data: 1
Hash: 0215c646e6a3faac0f954e8bf88263816aefefdbeeb9363f6c7b8b388b2303a6
Prev Hash: 0166be9d913999feea4fc94b818202d6d2b12484c201a6f223b311fcc6b385fd
Nonce: 36
is Validated: true
TimeStamp: 1642923947
Data: 2
Hash: 00b2aac6a4f0130af48d941ef153cf5c4c64d7a17f9b71ba17d2f1995d55cc33
Prev Hash: 0215c646e6a3faac0f954e8bf88263816aefefdbeeb9363f6c7b8b388b2303a6
Nonce: 47
is Validated: true
TimeStamp: 1642923947
Data: 3
Hash: 02913ec4213f7329574ce2a8cc25b0a0e92b00a40e35403cc48a68ffe21075a8
Prev Hash: 00b2aac6a4f0130af48d941ef153cf5c4c64d7a17f9b71ba17d2f1995d55cc33
Nonce: 21
is Validated: true
TimeStamp: 1642923947
Data: 4
Hash: 026d60bcf8b6ff1652e2c065c593162e109070b91fba291d0e3b5e3261e281fd
Prev Hash: 02913ec4213f7329574ce2a8cc25b0a0e92b00a40e35403cc48a68ffe21075a8
Nonce: 0
is Validated: true
TimeStamp: 1642923947
Data: 5
Hash: 00f04419689bcefb43fe386381f81dcf86e5c72a91c43444e015f22863ad1e39
Prev Hash: 026d60bcf8b6ff1652e2c065c593162e109070b91fba291d0e3b5e3261e281fd
Nonce: 137
is Validated: true
TimeStamp: 1642923947
Data: 6
Hash: 005c4a22a662cc653f6d60bd19eeb6f243705298e76bc485fa22d91c7edb2283
Prev Hash: 00f04419689bcefb43fe386381f81dcf86e5c72a91c43444e015f22863ad1e39
Nonce: 188
is Validated: true
TimeStamp: 1642923947
Data: 7
Hash: 020868cc9190e17d1b9a9e05f27ba4358d5a6c4a0491446ba45628ccfbd3239b
Prev Hash: 005c4a22a662cc653f6d60bd19eeb6f243705298e76bc485fa22d91c7edb2283
Nonce: 320
is Validated: true
TimeStamp: 1642923947
Data: 8
Hash: 01cebbd78196c674e64a50854b1ec9be7da8c0804e47d43721fee51589e5a81d
Prev Hash: 020868cc9190e17d1b9a9e05f27ba4358d5a6c4a0491446ba45628ccfbd3239b
Nonce: 113
is Validated: true
TimeStamp: 1642923947
Data: 9
Hash: 03a312dd6b769a858733a0f3131b02084e90c30d03b338f1ea529734e3703245
Prev Hash: 01cebbd78196c674e64a50854b1ec9be7da8c0804e47d43721fee51589e5a81d
Nonce: 53
is Validated: true
https://github.com/hou27/blockchain_go/tree/part2
참고자료
'Blockchain' 카테고리의 다른 글
Go로 만드는 블록체인 part 5 - Wallet (0) | 2022.01.29 |
---|---|
Go로 만드는 블록체인 part 4 - Transactions (0) | 2022.01.26 |
Go로 만드는 블록체인 part 3 - Persistence (0) | 2022.01.23 |
Go로 만드는 블록체인 part 1 - Base of Blockchain (0) | 2021.12.09 |
Go로 만드는 블록체인 part 0 (0) | 2021.12.05 |