go语言模拟区块链
实现思路
1.定义区块结构
第一阶段: 先实现基础字段:前区块哈希,哈希,数据
第二阶段: 补充字段:Version,时间戳,难度值等
2.创建一个区块(提供一个方法)
3.定义区块链结构
4.提供一个创建区块链的方法
5.提供一个向区块链中添加区块的方法
6.打印区块链
定义区块,创建区块
type Block struct {
// 前区块哈希
PrevHash []byte
// 哈希, 为了方便,我们将当前区块的哈希放入Block中
Hash []byte
//数据
Data []byte
}
//创建一个区块(提供一个方法)
//输入:数据,前区块的哈希值
//输出:区块
func NewBlock(data string, prevHash []byte) *Block {
b := Block{
PrevHash: prevHash,
Hash: nil,
Data: []byte(data),
}
//计算哈希值
//TODO
return &b
}
定义区块链,并创建
//定义区块链结构(使用数组模拟区块链)
type BlockChain struct {
Blocks []*Block //区块链
}
//创世语
const genesisInfo = "The first block"
//提供一个创建区块链的方法
func NewBlockChain() *BlockChain {
//创建BlockChain,同时添加一个创世块
genesisBlock := NewBlock(genesisInfo, nil)
bc := BlockChain{
Blocks: []*Block{genesisBlock},
}
return &bc
}
main
func main() {
bc := NewBlockChain()
//变量区块数据
for i, block := range bc.Blocks {
fmt.Printf("当前区块高度: %d\n", i)
fmt.Printf("PrevHash : %x\n", block.PrevHash)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", string(block.Data))
}
}
实现setHash
//提供计算区块哈希值的方法
func (b *Block) setHash() {
//比特币哈希算法:sha256
// data 是block各个字段拼成的字节流
// Join(a []string, sep string) string
//拼接三个切片,使用bytes.Join,接收一个二维的切片,使用一维切片拼接
// Join(s [][]byte, sep []byte) []byte
tmp := [][]byte{
b.PrevHash,
b.Hash,
b.Data,
}
//使用join方法,将二维切片转为1维切片
data := bytes.Join(tmp, []byte{})
hash := sha256.Sum256(data)
b.Hash = hash[:]
}
注意这里没有使用append,而是用的bytes.Join()
在NewBlock
中加入计算哈希值得方法
func NewBlock(data string, prevHash []byte) *Block {
...
//计算哈希值
b.setHash()
return &b
}
实现AddBlock方法
//提供一个向区块链中添加区块的方法
//参数:数据,不需要提供前区块的哈希值,因为bc可以通过自己的下标拿到
func (bc *BlockChain) AddBlock(data string) {
//通过下标,得到最后一个区块
lastBlock := bc.Blocks[len(bc.Blocks)-1]
//最后一个区块哈希值是新区块的前哈希
prevHash := lastBlock.Hash
//创建block
newBlcok := NewBlock(data, prevHash)
//添加bc中
bc.Blocks = append(bc.Blocks, newBlcok)
}
在main
中
func main() {
bc := NewBlockChain()
//变量区块数据
bc.AddBlock("今天是6月27日")
bc.AddBlock("今天是6月28日")
for i, block := range bc.Blocks {
fmt.Printf("当前区块高度: %d\n", i)
fmt.Printf("PrevHash : %x\n", block.PrevHash)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", string(block.Data))
}
}
补充Block字段
type Block struct {
//版本号
Version uint64
// 前区块哈希
PrevHash []byte
//交易的根哈希值
MerkleRoot []byte
//时间戳
TimeStamp uint64
//难度值, 系统提供一个数据,用于计算出一个哈希值
Bits uint64
//随机数,挖矿要求的数值
Nonce uint64
// 哈希, 为了方便,我们将当前区块的哈希放入Block中
Hash []byte
//数据
Data []byte
}
对应的修改NewBlock函数
func NewBlock(data string, prevHash []byte) *Block {
b := Block{
Version: 0,
PrevHash: prevHash,
MerkleRoot: nil, //随意写的
TimeStamp: uint64(time.Now().Unix()),
Bits: 0, //随意写的
Nonce: 0, //随意写的
Hash: nil,
Data: []byte(data),
}
//计算哈希值
b.setHash()
return &b
}
对应修改setHash方法
这时需要创建一个函数工具,用来将uint64转为[]byte
func uintToByte(num uint64) []byte {
var buffer bytes.Buffer
//使用二进制编码
// Write(w io.Writer, order ByteOrder, data interface{}) error
err := binary.Write(&buffer, binary.LittleEndian, &num)
if err != nil {
fmt.Println("binary.Write err :", err)
return nil
}
return buffer.Bytes()
}
func (b *Block) setHash() {
tmp := [][]byte{
uintToByte(b.Version), //将uint64转换为[]byte
b.PrevHash,
b.MerkleRoot,
uintToByte(b.TimeStamp),
uintToByte(b.Bits),
uintToByte(b.Nonce),
b.Hash,
b.Data,
}
//使用join方法,将二维切片转为1维切片
data := bytes.Join(tmp, []byte{})
hash := sha256.Sum256(data)
b.Hash = hash[:]
}
修改main函数
func main() {
bc := NewBlockChain()
//变量区块数据
time.Sleep(1 * time.Second)
bc.AddBlock("26号btc暴涨20%")
time.Sleep(1 * time.Second)
bc.AddBlock("27号btc暴涨10%")
time.Sleep(1 * time.Second)
for i, block := range bc.Blocks {
fmt.Printf("\n+++++++++ 当前区块高度: %d ++++++++++\n", i)
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PrevHash : %x\n", block.PrevHash)
fmt.Printf("MerkleRoot : %x\n", block.MerkleRoot)
fmt.Printf("TimeStamp : %d\n", block.TimeStamp)
fmt.Printf("Bits : %d\n", block.Bits)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
}
}