本文将要用go语言实现一个最简单的区块链,更多内容请参考https://jeiwan.cc/posts/building-blockchain-in-go-part-1/。
1 区块
区块是区块链组成单元,区块链就是由一个一个区块串联而成。区块链是一个不断向后延伸的区块的链表。这里定义一个最简单的区块结构:
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
Timestamp是时间戳,保存创建这个区块的时间戳。Data是这个区块要保存的数据。Hash是对这个区块数据进行Hash运算得到的Hash值。PrevBlockHash是前面一个区块的Hash值。
如何计算区块Hash?计算区块Hash是区块链的一个重要特性,它也是保证区块链安全的手段。计算Hash值是一种很耗费计算资源的操作,它增加了向区块链增加区块的难度,这是增进区块链安全的一种手段。
我们使用SHA-256算法来计算Hash值,SHA-256可以保证对任意长度的数据,计算得到的Hash值都是256位。它满足不可逆性,即很难从Hash值去推导元数据。只要对元数据进行一点小改动,Hash值将会变化很大。我们的计算区块Hash的函数为:
func (b *Block) SetHash() {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
hash := sha256.Sum256(headers)
b.Hash = hash[:]
}
这个函数把block的Timestamp、Data和PrevBlockHash拼接起来再计算hash值。
创建一个新区快的方法是:
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
block.SetHash()
return block
}
2 区块链
区块链是区块的链表,在这里我们使用数组来表示区块链。我们定义的类型为:
type Blockchain struct {
blocks []*Block
}
给Blockchain增加添加新区快的方法:
func (bc *Blockchain) AddBlock(data string) {
prevBlock := bc.blocks[len(bc.blocks)-1]
newBlock := NewBlock(data, prevBlock.Hash)
bc.blocks = append(bc.blocks, newBlock)
}
区块链是一个不断向后生成的链表,所以顾名思义就有第一个区块,称之为创世区块(genesis block),定义生成创世区块的方法:
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block", []byte{})
}
可以定义生成Blockchain的方法了:
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock()}}
}
3 让区块链工作起来
所有代码:
package main
import (
"strconv"
"bytes"
"crypto/sha256"
"time"
"fmt"
)
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
func (b *Block) SetHash() {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
hash := sha256.Sum256(headers)
b.Hash = hash[:]
}
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
block.SetHash()
return block
}
type Blockchain struct {
blocks []*Block
}
func (bc *Blockchain) AddBlock(data string) {
prevBlock := bc.blocks[len(bc.blocks)-1]
newBlock := NewBlock(data, prevBlock.Hash)
bc.blocks = append(bc.blocks, newBlock)
}
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block", []byte{})
}
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock()}}
}
func main() {
bc := NewBlockchain()
bc.AddBlock("Send 1 BTC to Ivan")
bc.AddBlock("Send 2 more BTC to Ivan")
for _, block := range bc.blocks {
fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Hash: %x\n", block.Hash)
fmt.Println()
}
}
运行结果:
Prev. hash:
Data: Genesis Block
Hash: b81cf38880f767af3c18d4a5417400a2fd2378c47d4ecb32e525cf1eedfb4a4f
Prev. hash: b81cf38880f767af3c18d4a5417400a2fd2378c47d4ecb32e525cf1eedfb4a4f
Data: Send 1 BTC to Ivan
Hash: 4cec5aa5b6503ac96d8b9a5e27159d32b954d262499fca88c4cc6f0beb454458
Prev. hash: 4cec5aa5b6503ac96d8b9a5e27159d32b954d262499fca88c4cc6f0beb454458
Data: Send 2 more BTC to Ivan
Hash: 866b1368c058b76dc0c88826aa3531b42803d481d22bfb6cca1361885a179674
4 结论
在这篇文章里我门创建了一个最简单的区块链,我门的区块链只是一个区块的数组,每个区块保存上一个区块的Hash。实际的区块链远比这个复杂。在我门这个区块链中,添加新区快非常容易,但是在实际的区块链中添加新区块需要做大量有难度的计算工作以获得添加新区快的权限。此外,由于区块链不会只有一个节点,一个新区块被矿工生产出来,还需要经过其它节点的验证才能写入数据库。此外,我们的区块链中还没有添加交易。
这些都将在后续文章实现。