a simplized blockchain demo
-
定义一个区块的结构Block
a. 区块头:6个字段
b. 区块体:字符串表示data
-
提供一个创建区块的方法 NewBlock(参数)
- 定义一个区块链结构BlockChain Block数组
- 提供一个创建BlockChain的方法 NewBlockChain()
- 提供一个添加区块的方法 AddBlock(参数)
创建文件block.go,添加如下代码:
type Block struct {
//区块头:
//比特币网络的版本号
Version uint64
//当前区块的哈希,注意,这是为了方便写代码而加入的。比特币不在区块中存储当前区块的哈希值
Hash []byte
//前区块的哈希值,用于连接链条
PrevBlockHash []byte
//梅克尔根,用于快速校验区块,校验区块的完整性。
MerkleRoot []byte
//时间戳,表示区块创建的时间
TimeStamp uint64
//难度值,调整挖矿难度
Difficuty uint64
//随机值,挖矿需要求的数字
Nonce uint64
//区块体:
//区块数据
Data []byte
}
继续在block.go中添加代码:
func NewBlock(data string, prevHash []byte) Block {
var block Block
block = Block{
Version: 0,
PrevBlockHash: prevHash,
Hash: []byte{},
MerkleRoot: []byte{},
TimeStamp: uint64(time.Now().Unix()),
Difficuty: 10,//随便写,目前不用
Nonce: 10,//随便写,目前不用
Data: []byte(data)}
return block
}
注,此时hash值先填写空的,后面会处理。
创建blockchain.go文件,添加如下代码:
//- 定义一个区块链结构BlockChain
//Block数组
type BlockChain struct {
blocks []*Block
}
继续在blockchain.go中添加代码:
//- 提供一个创建BlockChain的方法
func NewBlockChain() *BlockChain {
//创世块产生
block := NewBlock("Genesis Block!", []byte{})
//添加到区块链数组
return &BlockChain{blocks:[]*Block{&block}}
}
继续在blockchain.go中添加代码:
//- 提供一个添加区块的方法
//AddBlock(参数)
func (bc *BlockChain)AddBlock(data string) {
//获取最后一个区块
lastBlock := bc.blocks[len(bc.blocks) -1]
//获取最后一个区块的哈希,作为最新(当前)区块的前哈希
prevHash := lastBlock.Hash
block := NewBlock(data, prevHash)
bc.blocks = append(bc.blocks, &block)
}
创建main.go,添加如下代码:
import (
"fmt"
)
func main() {
bc := NewBlockChain()
bc.AddBlock("HelloWorld!")
bc.AddBlock("Hello Itcast!")
//block的数组
for index, block := range bc.blocks {
fmt.Println(" ============== current block index :", index)
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("MerkleRoot : %x\n", block.MerkleRoot)
fmt.Printf("TimeStamp : %d\n", block.TimeStamp)
fmt.Printf("Difficuty : %d\n", block.Difficuty)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Data : %s\n", block.Data)
}
}
go build -o block *.go
./block
==可以看到目前三个区块都已经打印出来了,但是没有哈希值,这几个区块相对独立,所以接下来要实现哈希值的计算,将几个区块链接起来。==
回到block.go文件,加入如下代码,求区块哈希,==由于没有进行挖矿,所以哈希值不是N个0前导的,仅仅是哈希值而已,挖矿在后续课程中介绍==。
func (block *Block) setHash() {
//把block的数据进行拼接,然后再做哈希运算!
//strings.Join([]string, string)
//["hello", "world", "itcast"], "==" ==>hello=world=itcast
//["hello", "world", "itcast"], "" ==> helloworlditcast
tmp := [][]byte{
uintToByte(block.Version),
block.PrevBlockHash,
//block.Hash, hash目前是空的,不需要填写
block.MerkleRoot,
uintToByte(block.TimeStamp),
uintToByte(block.Difficuty),
uintToByte(block.Nonce),
block.Data}
data := bytes.Join(tmp, []byte{})
//[ []byte1, []byte2, []byte3 ] , []byte4 ==> byte1 byte4 byte2 byte4 byte3 byte4
//[ []byte1, []byte2, []byte3 ] , []byte4 ==> byte1 byte2 byte3
hash := sha256.Sum256(data /*[]byte*/)
block.Hash = hash[:]
}
在NewBlock函数中添加如下代码
block.setHash()
具体位置如下:
这样就实现了最基本的区块哈希计算。
在setHash中涉及到类型转换函数uintToByte
,用于将unit64转换成[]byte字节流,请添加值block.go中,定义如下:
func uintToByte(num uint64) []byte {
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian, num)
if err != nil {
fmt.Println("err happend!")
os.Exit(1)
}
return buffer.Bytes()
}
重新编译,执行(参考步骤七),结果如下
============== current block index : 0
Version : 0
PrevBlockHash :
Hash : 2c9b54532e99264119a6b50ecbb7ad0d8cb0450c31ca5ec6d59478e4d783f59d
MerkleRoot :
TimeStamp : 1536280290
Difficuty : 10
Nonce : 10
Data : Genesis Block!
============== current block index : 1
Version : 0
PrevBlockHash : 2c9b54532e99264119a6b50ecbb7ad0d8cb0450c31ca5ec6d59478e4d783f59d
Hash : ecd0c5a9f16652affc32fb8fa1bcc210c7272e8558928b90bb860a2f28084fa5
MerkleRoot :
TimeStamp : 1536280290
Difficuty : 10
Nonce : 10
Data : HelloWorld!
============== current block index : 2
Version : 0
PrevBlockHash : ecd0c5a9f16652affc32fb8fa1bcc210c7272e8558928b90bb860a2f28084fa5
Hash : d7dc63985c34ae4306c44a3c9e5cf3e2cc561d22c1207b2d59ab98f6897c2a90
MerkleRoot :
TimeStamp : 1536280290
Difficuty : 10
Nonce : 10
Data : Hello Itcast!
v1完,别走开,后面更精彩!
挖矿