๋นํธ์ฝ์ธ์ ๋ชจ๋ ๋ธ๋ก์ ํธ๋์ญ์
๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ค๋ฉด ํฐ space๊ฐ ํ์ํฉ๋๋ค. ๊ฒ์ฆ์ธ๋ค์ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ์ ์ฅํ ํ์๊ฐ ์์ง๋ง, ๋นํธ์ฝ์ธ์ ์ฌ์ฉํ๋ ๋ชจ๋ ์ฌ๋์ด ๋ชจ๋ ํธ๋์ญ์
๋ฐ์ดํฐ(์๋ฐฑ GB)๋ฅผ ์ ์ฅํด์ผ ํ๋ค๋ฉด ์๋ฌด๋ ๋นํธ์ฝ์ธ ๊ฑฐ๋๋ฅผ ์ด์ฉํ์ง ์์ ๊ฒ ์
๋๋ค.
๋นํธ์ฝ์ธ์ ์ด๋ฌํ ๋นํจ์จ์ฑ์ ์ค์ด๊ธฐ์ํด Merkle Tree๋ฅผ ๋์
ํ์ฌ ์ฌ์ฉํฉ๋๋ค. ๋จธํดํธ๋ฆฌ์ ๋ํ ๊ฐ๋
์ ๋งํฌ์์ ๋จผ์ ๊ณต๋ถํ์๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
๊ตฌํํ ๊ฒ
๋จธํดํธ๋ฆฌ์ ๊ตฌ์กฐ๋ ์๋ ์ํคํผ๋์์์ ๊ฐ์ ธ์จ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด Leaf ๋
ธ๋๋ถํฐ ํด์๋ฅผ ๊ตฌํ๊ณ 2๊ฐ์ ํด์๋ฅผ ์ด์ด์ ๋ค์ ํด์๋ฅผ ๊ตฌํด ๋ถ๋ชจ๋
ธ๋๋ฅผ ๋ง๋ค์ด๊ฐ๋ ๊ตฌ์กฐ์
๋๋ค. Leaf ๋
ธ๋๋ ์ง์๊ฐ ์กด์ฌํด์ผํฉ๋๋ค.
ํด์์ ํน์ฑ์ Leaf ์ค์ ํ๋์ ๋ฐ์ดํฐ(ํด์๊ฐ)๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๊ทธ ๋ฐ์ดํฐ์ ์ฐ๊ด๋ ๋ชจ๋ ๋ถ๋ชจ์ ํด์๊ฐ์ด ๋ฐ๋๋๋ค.
Coinbase Transaction on New Block
Merkle Tree๋ฅผ ์ถ๊ฐํ๊ธฐ์ ์ ์๋ก์ด ๋ธ๋ก์ด ์์ฑ๋ ๋ ์ฝ์ธ๋ฒ ์ด์ค ํธ๋์ญ์
์ด ๋ฐ์ํ๋๋ก ์ฝ๋๋ฅผ ์์ ํฉ๋๋ค.
์ผ๋จ transaction.go
ํ์ผ์ ์ด์ด CoinbaseTx์ ๋ฐ์ดํฐ๋ฅผ ๋๋คํ๊ฒ ์์ฑ๋๋๋ก ๋ฐ๊ฟ๋๋ค.
// miningํ๋ฉด to์๊ฒ ์ฝ์ธ์ ๋ณด์์ผ๋ก ์ฃผ๋ Coinbase Transaction.
func CoinbaseTx(to, data string) *Transaction {
if data == "" {
randData := make([]byte, 24)
_, err := rand.Read(randData)
Handle(err)
data = fmt.Sprintf("%x", randData)
}
txin := TxInput{[]byte{}, -1, nil, []byte(data)}
txout := NewTXOutput(20, to)
tx := Transaction{nil, []TxInput{txin}, []TxOutput{*txout}}
tx.ID = tx.Hash()
return &tx
}
send์์ ๋ธ๋ก์ ์ถ๊ฐํ ๋ Coinbase ํธ๋์ญ์
์ ์ถ๊ฐํ์ฌ ๋ธ๋ก์ ์ถ๊ฐํ๋ ์ฃผ์์๊ฒ ๋ณด์ 20 ์ฝ์ธ์ ์ฃผ๋๋ก ํฉ๋๋ค. cli/cli.go
// {from}์์ {to}๋ก {amount}๋งํผ ๋ณด๋
๋๋ค.
func (cli *CommandLine) send(from, to string, amount int) {
if !wallet.ValidateAddress(from) {
log.Panic("Address is not Valid")
}
if !wallet.ValidateAddress(to) {
log.Panic("Address is not Valid")
}
chain := blockchain.ContinueBlockChain("") // blockchain์ DB๋ก ๋ถํฐ ๋ฐ์์จ๋ค.
UTXOset := blockchain.UTXOSet{chain}
defer chain.Database.Close()
cbTx := blockchain.CoinbaseTx(from, "") // ์ฝ์ธ๋ฒ ์ด์ค ํธ๋์ญ์
์ ์์ฑํ๊ณ
tx := blockchain.NewTransaction(from, to, amount, &UTXOset) // send ํธ๋์ญ์
๋ ์์ฑํ์ฌ
block := chain.AddBlock([]*blockchain.Transaction{cbTx, tx}) // ์๋ก์ด ๋ธ๋ก์ ์ถ๊ฐํฉ๋๋ค.
UTXOset.Update(block)
fmt.Println("Success!")
}
blockchain/blockchain.go
ํ์ผ์ ์ด์ด์ VerifyTransaction์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํฉ๋๋ค.
// ํธ๋์ญ์
์ ๊ฒ์ฆํฉ๋๋ค.
func (chain *BlockChain) VerifyTransaction(tx *Transaction) bool {
// Coinbase ํธ๋์ญ์
์ ์ ํจํ๋ค๊ณ ํ๋จํฉ๋๋ค.
if tx.IsCoinbase() {
return true
}
prevTXs := make(map[string]Transaction)
for _, in := range tx.Inputs {
prevTX, err := chain.FindTransaction(in.ID)
Handle(err)
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
}
// ์ด์ ๊ฑฐ๋ ๊ธฐ๋ก์ ์ด์ฉํด์ ๊ฒ์ฆํฉ๋๋ค.
return tx.Verify(prevTXs)
}
blockchain/merkle.go
Merkle Tree๋ ํด์๋ค์ ํด์๋ก ์๊ฐํ ์ ์์ต๋๋ค. ๋ฆฌํ๋
ธ๋(Leaf node)๋ ํ๋์ ํธ๋์ญ์
์ ํด์๋ฅผ ์ ์ฅํ๋ฉฐ, ๋๊ฐ์ ๋ฆฌํ๋
ธ๋์ ํด์๊ฐ์ ํฉํ์ฌ ์๋ก์ด ๋ถ๋ชจ๋
ธ๋๋ฅผ ๋ง๋ค์ด๋
๋๋ค. ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฃจํธ๋
ธ๋๊น์ง ๋ง๋ค๊ณ Merkle Tree๋ ์ด ๋ฃจํธ๋
ธ๋๋ฅผ ํฌ์ธํ
ํฉ๋๋ค.
package blockchain
import (
"crypto/sha256"
)
// MerkleTree์ ๋ฃจํธ๋ฅผ ์ ์ฅํ๋ ์คํธ๋ญ์ณ
type MerkleTree struct {
RootNode *MerkleNode
}
// MerkleTree์ ๊ฐ๋ณ ๋
ธ๋
type MerkleNode struct {
Left *MerkleNode // ์ผ์ชฝ ์์
Right *MerkleNode // ์ค๋ฅธ์ชฝ ์์
Data []byte // hash ๊ฐ
}
// MerkleNode๋ฅผ ์์ฑํ๋ ํจ์
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
node := MerkleNode{}
// {left}, {right}๊ฐ ์๋ค๋ฉด leaf node
if left == nil && right == nil {
hash := sha256.Sum256(data)
node.Data = hash[:]
} else {
// ์์๋ค์ ํด์๋ฅผ ์ด์ด์ ๋ค์ Hash๋ฅผ ๊ตฌํจ
prevHashes := append(left.Data, right.Data...)
hash := sha256.Sum256(prevHashes)
node.Data = hash[:]
}
// ์์ ์ฐ๊ฒฐ
node.Left = left
node.Right = right
return &node
}
// MerkleTree๋ฅผ ์์ฑํ๋ ๊ณผ์
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
// ์์ ๋
ธ๋์ ์๋ฅผ ์ง์๋ก ๋ง๋ค์ด์ผํจ
// ๋ง์ง๋ง ์์์ ๋ณต์ฌํ๋ค.
if len(data)%2 != 0 {
data = append(data, data[len(data)-1])
}
// Leaf node๋ฅผ ๋ง๋๋ ๊ณผ์
for _, dat := range data {
node := NewMerkleNode(nil, nil, dat)
nodes = append(nodes, *node)
}
// Tree height ๋งํผ ์ํ
for i := len(data); i > 1; i /= 2 {
var level []MerkleNode
// ์์๋๋ก 2๊ฐ์ฉ ํฉ์ณ์ ๋
ธ๋ ์์ฑ
for j := 0; j < len(nodes); j += 2 {
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
level = append(level, *node)
}
// ๋ค์ iteration์ ์๋ก ๋ง๋ค์ด์ง ๋
ธ๋๋ค๋ก ์งํ
nodes = level
}
// Root ๋
ธ๋ ๋ฐํ
tree := MerkleTree{&nodes[0]}
return &tree
}
blockchain/block.go
HashTransactions ์ฝ๋๋ฅผ ๋ฐ๊พธ์ด ๋จธํดํธ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๊ณ ๋ฃจํธ์ Hash๊ฐ์ ๋ฐํํ๋๋ก ๋ณ๊ฒฝํฉ๋๋ค.
// Block์ Transaction๋ค์ ํฉ์ณ์ ํ๋์ ํด์๋ฅผ ๋ง๋ญ๋๋ค.
// Merkle Tree๋ฅผ ์ด์ฉํฉ๋๋ค.
func (b *Block) HashTransactions() []byte {
var txHashes [][]byte
for _, tx := range b.Transactions {
txHashes = append(txHashes, tx.Serialize())
}
tree := NewMerkleTree(txHashes)
// merkle tree๋ฅผ ๊ตฌ์ฑํ๊ณ ๋ฃจํธ๋
ธ๋์ ๋ฐ์ดํฐ ๊ฐ์ด ์ต์ข
ํด์๊ฐ
return tree.RootNode.Data
}
Test
Send ์์ ๋ธ๋ก์ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ ์ฝ์ธ๋ฒ ์ด์ค ํธ๋์ญ์
์ด ๋ฐ์ํ์ฌ 20์ฝ์ธ์ด ๋ ์ฃผ์ด์ง๋ค.
printchain
์ผ๋ก ์์ธํ ๋ด์ฉ์ ๋ณด๋ฉด,
2๋ฒ์งธ ๋ธ๋ก (ํ๋ฆฐํธ ์์์๋ ์์์ ์ฒซ๋ฒ์งธ)์ 2๊ฐ์ transaction์ด ์ ํ์๊ณ , ๊ทธ ์ค ์ฒซ๋ฒ์งธ ํธ๋์ญ์
์ Coinbase ํธ๋์ญ์
์์ ์ ์ ์๋ค.
๋ฏธ๊ตฌํ ์ฌํญ
ํ ๋
ธ๋์ ๋ผ์ดํธ ๋
ธ๋ ๊ฐ๋
์ด ์์ด, ์ผ๋จ ํ๋์ ๋
ธ๋๊ฐ ๋ชจ๋ ๋ธ๋ก์ฒด์ธ ์ ๋ณด๋ฅผ ๋ค ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ๋จธํดํธ๋ฆฌ์ ์ฅ์ ์ ํ์ฉํ์ง ๋ชปํ๋ ์ํฉ์
๋๋ค.
Last updated: May 8, 2021