版权声明:本文为作者原创,如需转载,请注明出处
https://blog.csdn.net/weixin_42940826
前言
在IT界,2018最火的热词相必就是区块链了,C++和GO是目前最适合区块链开发的两种语言,所以咱们学GO的肯定得学一点区块链的知识,但是区块链涉及太多密码学,金融学、p2p网络等知识了,从哪里切入呢,今天我们就从用go实现一条带有模拟挖矿系统的简单区块链。
代码分析
三大模块
代码还是比较简单清晰的,主要有三个模块,Block模块、BlockChain模块、POW模块即挖矿模块。
运行流程
- 首先从定义一个区块开始,一个区块中包含的信息有区块信息,时间戳,前区块哈希值,现区块哈希值,经过挖矿后得到的哈希碰撞值等等。
- 接着我们开启一个切片用来存放一个个区块,但是第一个区块是比较特殊的,我们称之为创世区块,在真实的比特币中,第一个区块是由创始人中本聪挖矿所得,没有前哈希值,所以这里我们直接将第一个创世区块创建添加到区块切片中。
- 然后给到一个添加区块的方法,用户添加区块信息后,经过POW挖矿系统才能成功添加到区块链上。
有了大概的运行流程接下来再来看代码就会轻松很多了。
代码
package main
import (
"math/big"
"bytes"
"encoding/binary"
"crypto/sha256"
"fmt"
"time"
)
//Block模块
type Block struct {
Version uint64 //版本号
MerkelRoot []byte //这是一个默克尔数,这里先默认为空
TimeStamp string //时间戳
Difficulty uint64 //难度值
Nonce uint64 //挖矿所找到的随机数
PrevBlockHash []byte//前区块哈希值
Data []byte //插入的数据
Hash []byte //当前区块哈希值
}
//给到一个创建新区块的方法
func newBlock(data,prehash []byte)*Block {
block:=Block{
Version:00,
MerkelRoot:[]byte{},
TimeStamp:time.Now().Format("2006-15:04:05"),
Difficulty:difficulty,
Data:data,
PrevBlockHash:prehash,
}
//需要被挖矿之后才能创建区块,所以调用挖矿函数
pow:=NewPOW(&block)
nonce,hash:=pow.Mine()
//挖矿结束,得到哈希碰撞值
block.Nonce=nonce
block.Hash=hash
return &block
}
//Blockchain模块
const gnnesinfo="1996年9月2日,一代伟人诞生了"
//给到一个区块链结构
type Blockchain struct {
blocks []*Block
}
//将创世区块加入区块链,并返回一条可供操作的区块链
func NewblockChain()*Blockchain {
var bc Blockchain
block:=newBlock([]byte(gnnesinfo),[]byte{})
bc.blocks=append(bc.blocks,block)
return &bc
}
//给到一个增加区块的方法
func (this *Blockchain)Addblock(data []byte) {
lastblockhash:=this.blocks[len(this.blocks)-1].Hash
block:=newBlock(data,lastblockhash)
this.blocks=append(this.blocks,block)
}
//遍历,打印所有
func (this *Blockchain)PrintAll() {
for i,v:=range this.blocks {
fmt.Printf("=========区块高度%d=========\n",i)
fmt.Printf("Version : %d\n", v.Version)
fmt.Printf("PrevBlockHash : %x\n", v.PrevBlockHash)
fmt.Printf("Hash : %x\n", v.Hash)
fmt.Printf("MerkleRoot : %x\n", v.MerkelRoot)
fmt.Printf("TimeStamp : %s\n", v.TimeStamp)
fmt.Printf("Difficuty : %d\n", v.Difficulty)
fmt.Printf("Nonce : %d\n", v.Nonce)
fmt.Printf("Data : %s\n", v.Data)
}
}
//pow挖矿模块
const difficulty=24
//POW挖矿结构需要两个参数,一个是所需挖矿的区块,另一个是挖矿成功所需目标数字
type ProofOfWork struct {
target *big.Int
block *Block
}
//给到一个根据难度值得到哈希碰撞目标值的函数
func Gettargetint()*big.Int {
targetint:=big.NewInt(1)
targetint.Lsh(targetint,256-difficulty)
return targetint
}
//创建挖矿的方法
func NewPOW(block *Block)*ProofOfWork {
var this ProofOfWork
this.block=block
targetint:=Gettargetint()
this.target=targetint
return &this
}
//一个用来将uint64转化为字符切片的小函数,方便接下来的转化
func uint2byte(num uint64)[]byte {
var buff bytes.Buffer
binary.Write(&buff,binary.BigEndian,&num)
return buff.Bytes()
}
//挖矿的准备工作,将其他字符组合起来之后求其哈希值
func (pow *ProofOfWork)PreparetoMine(nonce uint64)[]byte {
info:=[][]byte{
pow.block.PrevBlockHash,
pow.block.Data,
uint2byte(nonce),
uint2byte(pow.block.Version),
uint2byte(pow.block.Difficulty),
[]byte(pow.block.TimeStamp),
pow.block.MerkelRoot,
}
allinfo:=bytes.Join(info,[]byte{})
hash:=sha256.Sum256(allinfo)
return hash[:]
}
//pow挖矿方法,返回两个参数,一个是碰撞成功的数字nonce,另一个是当前区块哈希值
func (pow *ProofOfWork)Mine()(uint64,[]byte) {
var nonce uint64
//nonce从0开始穷举,直到出现哈希值小于给到的目标值
var hash []byte
for {
hash=pow.PreparetoMine(nonce)
var hashint big.Int
hashint.SetBytes(hash)
//对比哈希值是否小于目标值,小于则成功退出
if hashint.Cmp(pow.target)==-1 {
break
}
//不小于则继续穷举
nonce++
}
return nonce,hash
}
//调用
func main() {
bc:=NewblockChain()
bc.Addblock([]byte("welcometo"))
bc.Addblock([]byte("mycsdnblog"))
bc.PrintAll()
}
以上就是一个微型的区块链了,虽然看起来很简单,但是对于理解比特币、区块链还是有不少帮助的。
若是感兴趣或者不屑一顾的大牛们可以戳我的githubGO语言实现比特币系统,是这个小区块链的升级版本,并且将持续维护更新,加入了命令行功能、UTXO转账机制,bolt数据库存储、钱包等,更加接近真正的比特币系统
也欢迎各位给我留言,共同学习: )