从零开始写一条公链(一)

写在前面:

本文目的:使用go语言,实现简单的区块结构,并通过pow共识算法将区块存储在区块链上,结构如下图

公链一1

Prehash :上一个区块的hash值,通过它将两个区块串联起来,以后遍历区块也要用到,同时也保证了区块的有效性与安全性 Timestamp:区块产生的时间 Data:区块上的记录,交易 Hash:当前区块的hash 注:这只是一个简单的区块结构,具体结构还要根据区块的作用来定义属性

实现一个区块

定义一个结构体

type Block struct {
	//时间戳
	Timestame int64
	//交易数据
	Data []byte
	//上一个区块hash
	Prehash []byte
	//区块hash
	Hash []byte
}

创建一个区块

根据文章开头的区块链结构,要创建一个区块需要传入上一个区块的hash值Prehash和交易数据Data,区块Hash需要通过将区块内所有数据通过sha256算法,计算得到

func  CreateBlock(data string,preHash []byte)(*Block) {
	timestame := time.Now().Unix()
	block := &Block{Timestame:timestame,Data:[]byte(data),Prehash:preHash}
	//区块hash由timestame,data,prehash值拼接后的值,sha256加密后得到的值
	block.SetHash()
	return block
}

计算Hash值

func (block *Block) SetHash() {
	//时间戳转byte数组
	timestameBytes := []byte(strconv.FormatInt(block.Timestame,10))
	//将区块转byte数组
	blockData := bytes.Join([][]byte{
		timestameBytes,
		block.Data,
		block.Prehash},[]byte{})
	//计算hash值
	hash := sha256.Sum256(blockData)
	block.Hash = hash[:]
}

注:实际上这里的Hash值是需要通过pow共识算法计算的,这里只是为了创建区块,暂时先这么写,下面再添加pow算法

在main()函数中执行下面的代码,创建一个区块

func main() {
	var preHash = [32]byte{}
	block := BLC.CreateBlock("zhqBlock1",preHash[:])
	block.PrintBlock()
}

输出结果:

公链一2

实现区块链

上面创建区块时,有一个问题就是区块的PreHash,是我手动添加的,这肯定是不对的,但是区块链的第一个区块是没有PreHash的,所以这第一个区块和其他区块相比有些特别,我们称它为创世区块,创世区块需要特别处理,preHash值设为0

创建创世区块

func CreateGenesisBlock()*Block  {
	//长度为32bits的byte数组,刚好256位与sha256得到的hash值长度对应
	var preHash = [32]byte{}
	block := CreateBlock("genesis block",preHash[:])
	return block
}

区块链结构体


type Blockchain struct {
	//链上所有数据应该是保存到本地数据库中,暂时先用切片简单实现
	blocks []*Block
}

创建区块链对象


func  CreateGenesisBlockchain()(*Blockchain) {
	//创建创世区块
	block := CreateGenesisBlock()
	//创建区块链
	blc := new(Blockchain)
	blc.blocks = append(blc.blocks,block)
	return blc
}

创建区块链的同时创建了创世区块

向区块链中添加区块:首先要有交易数据Data,其次上一个区块的hash即PreHash。Data是收到的交易数据,PreHash是区块链上最后一个区块的Hash值即blockchain.blocks[len(blockchain.blocks)-1].hash

func (blc *Blockchain) Addblock(data string)  {
	//获取链上最后一个区块的hash值
	lastBlock := blc.blocks[len(blc.blocks)-1]
	//将最后一个区块的hash值,传给新区块,做为prehash
	block := CreateBlock(data,lastBlock.Hash)
	blc.blocks = append(blc.blocks,block)
}

在main()中执行

func main() {
	//创建区块链
	blc := BLC.CreateGenesisBlockchain()
	//添加新区块
	blc.Addblock("first block")
	//添加新区块
	blc.Addblock("second block2")
	//打印区块链
	blc.PrintBlockchain()
}


运行结果;

公链一3

结果中可以看到上一个区块的hash与下一个区块的preHash是对应的

添加pow共识算法

首先修改一下Block结构体,新增属性Nonce,挖矿时通过不断累加Nonce值来找到有效的hash值

type Block struct {
	//时间戳
	Timestame int64
	//交易数据
	Data []byte
	//上一个区块hash
	Prehash []byte
	//区块hash
	Hash []byte
	//
	Nonce int64
}

设置挖矿难度

const kTargetDifficult = 16

修改SetHash()方法,找到一个有效的hash值

//挖矿
func PandingHash(block *Block) {
	for  {
		//将区块转byte数组
		timestameBytes := []byte(strconv.FormatInt(block.Timestame,10))
		nonce := []byte(strconv.FormatInt(block.Nonce,10))
		blockData := bytes.Join([][]byte{
			timestameBytes,
			block.Data,
			block.Prehash,
			nonce},[]byte{})
		hash := sha256.Sum256(blockData)
		fmt.Printf("\r%x",hash)
		//判断hash是否有效
		if IsValidHash(hash[:]) {
			//找到有效hash,跳出循环
			block.Hash = hash[:]
			fmt.Println()
			break
		}else {
			//hash值无效,累加nonce,继续挖矿
			block.Nonce++
		}
	}
}

//判断hash是否合法
func IsValidHash(hash []byte)bool  {
	target := big.NewInt(1)
	target.Lsh(target,256-kTargetDifficult)
	var hashInt big.Int
	hashInt.SetBytes(hash)
	if target.Cmp(&hashInt)== 1 {
		return true
	}
	return false
}

运行结果:

公链一

今天就到这里,这篇是通过数组维护区块链,下一篇介绍通过BoltDB来维护区块链

下载demo https://github.com/sweetMegan/PubliceChain1

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦