블록체인/메인넷

Go 언어로 블록체인 메인넷 만들기 - 프로토타입

블록체인을 공부하면서 메인넷이 어떻게 구성되는지는 알아둘 필요가 있다고 생각되어 Go 언어로 블록체인 메인넷을 간단하게 만들어보면 어떨가 싶어 포스트와 함께 진행하기로 했다. 그저 블록체인의 원리를 공부하면서 이를 코드로 구현하는 것 뿐이므로 완벽하다고는 할 수 없을 것이다. 다만 구현에 있어 참고한 것이 있다면 github.com/Jeiwan/blockchain_go 정도가 있다.

 

메인넷 부분은 이더리움, 클레이튼 등 이미 구현이 되어있는 블록체인 플랫폼 위에서 동작하는 디앱(Dapp)과는 다른 축에 있기 때문에 디앱이 웹개발에서 프론트엔드라면 메인넷은 백엔드 개발이라고 생각하면 편할 것이다. 이더리움의 예를 들때 실제로 디앱을 개발할 때는 web3.js 를 사용하여 이더리움 네트워크와 연결하고 UI 개발은 리액트 등의 프론트엔드 프레임워크를 사용하므로 프론트엔드 개발에 블록체인 지식이 일부 더해진다는 점을 빼면 그렇게까지 큰 차이는 없으니까 말이다.

type Block

블록체인은 블록들을 체인형태로 연결한 것이다. 거기에서 블록의 역할을 하게 되는 것이 Block 타입이다. 블록은 블록헤더와 블록바디 두 가지로 나눌 수가 있는데 이는 Http 프로토콜 메시지에서 헤더와 바디로 나눈 것과 유사하다. 비트코인에서 블록헤더에는 해시, 난이도, 논스, 이전블록해시, 타임스탬프, 머클트리 루트가 있으나, 프로토타입에서는 해시, 타임스탬프, 이전블록해시를 가지고 있으며 머클트리 루트 대신에 평범한 데이터를 가지게 하도록 하겠다.

type Block struct {
	PrevBlockHash []byte
	Hash          []byte
	Timestamp     int64
	Data          []byte
}

블록바디는 일반적으로 거래, 즉 트랜잭션들을 가지고 있게 되나 현재는 트랜잭션이 없으므로 생략한다.

func NewBlock(string, []byte) *Block

새로운 블록을 만든다. 외부에서 머클트리 루트를 대신할 데이터와 이전블록해시를 파라매터로 받는다. 여기서 주의해야 할 점은 Hash 는 별도로 설정해준다는 점이다.

func NewBlock(data string, prevBlockHash []byte) *Block {
	block := Block{prevBlockHash, []byte{}, time.Now().Unix(), []byte(data)}
	block.SetHash()

	return &block
}

func .SetHash()

블록의 해시를 설정한다. 기본적으로 블록의 해시는 블록 헤더들을 모아서 이를 해시함수를 통해 만들어낼 수 있다. 해시함수 중 하나인 SHA256 알고리즘을 사용하여 만들어보자. 해당 알고리즘을 적용하면 32바이트(256/8)의 해시 값이 반환 될 것이다.

func (b *Block) SetHash() {
	header := bytes.Join([][]byte{
		b.PrevBlockHash,
		b.Data,
		IntToHex(b.Timestamp),
	}, []byte{})

	hash := sha256.Sum256(header)
	b.Hash = hash[:]
}

블록헤더를 바이트로 묶어 해시화 시켜 블록에 지정함을 살펴보는 것이 좋다. IntToHex 함수의 경우 int64 타입의 값을 받아 []byte 로 반환해주도록 구성되어 있는데, 자세한 사항은 binary.Write() 함수를 참조하도록 하자.

type Blockchain

블록체인은 다수의 블록을 가진다. 애초에 블록체인 자체가 블록들의 연결이기 때문이다.

type Blockchain struct {
	blocks []*Block
}

func NewBlockchain() *Blockchain

블록체인을 새로 만들때 눈여겨 보아야하는 점은 제네시스 블록을 미리 포함시킨다는 점이다. 제네시스 블록은 블록체인의 가장 첫 블록을 의미한다. 제네시스 블록에는 이전블록이 없기 때문에 비워두도록하자.

func NewBlockchain() *Blockchain {
	return &Blockchain{[]*Block{NewBlock("Genesis Block", []byte{})}}
}

func .AddBlock(string)

블록체인에 새로운 블록을 포함시킨다. 블록을 생성하고 .blocks 배열에 추가하기만 하면 된다.

func (bc *Blockchain) AddBlock(data string) {
	block := NewBlock(data, bc.blocks[len(bc.blocks)-1].Hash)
	bc.blocks = append(bc.blocks, block)
}

func main()

이제 우리가 만든 블록체인을 한 번 출력해보자. 아직 트랜잭션, 합의 알고리즘, 주소, 네트워크 등 많은 것이 부족하지만 이 정도만 해도 기본적인 개념들은 적용되었고 할 수 있다.

func main() {
	bc := NewBlockchain()

	bc.AddBlock("Send 1 BTC to Ivan")
	bc.AddBlock("Send 2 more BTC to Ivan")

	for _, b := range bc.blocks {
		fmt.Printf("PrevBlockHash: %x\n", b.PrevBlockHash)
		fmt.Printf("Hash: %x\n", b.Hash)
		fmt.Printf("Data: %s\n", b.Data)

		fmt.Println()
	}
}
PrevBlockHash: 
Hash: e07b13b7ce173c50358f8cd57bc72989b92a9fc0e4e419170c4fbf059f97d222
Data: Genesis Block

PrevBlockHash: e07b13b7ce173c50358f8cd57bc72989b92a9fc0e4e419170c4fbf059f97d222
Hash: 8c10769ec880d288ce52faeb4be8c76b98eb2e98019e83fb93e87115498b3961
Data: Send 1 BTC to Ivan

PrevBlockHash: 8c10769ec880d288ce52faeb4be8c76b98eb2e98019e83fb93e87115498b3961
Hash: eaac541fc14447af42d067d70878b868f62b7cae80bdf2c68c598ede6ebaf094
Data: Send 2 more BTC to Ivan