//utils.go
package main

import (
	"bytes"
	"encoding/binary"
	"log"
)

func IntToHex(num int64)[]byte{
	buff:=new(bytes.Buffer)  //开辟内存,存储字节集
	err:=binary.Write(buff,binary.BigEndian,num)//num转化字节集写入
	if err!=nil{
		log.Panic(err)
	}
	return buff.Bytes() //返回字节集合
}
//block.go
package main

import (
	"time"
)

//定义区块
type Block struct{
	Timestamp int64      //时间线,1970年1月1日00.00.00
	Data []byte    //交易数据
	PrevBlockHash []byte  //上一块数据的哈希
	Hash []byte   //当前块数据的哈希
	Nonce int   //工作量证明
}

/*//设定结构体对象哈希
func (block *Block)SetHash(){
	//处理当前的时间,转化为10进制的字符串,再转化为字节集合
	timestamp:=[]byte(strconv.FormatInt(block.Timestamp,10))
	//叠加要哈希的数据
	headers:=bytes.Join([][]byte{block.PrevBlockHash,block.Data,timestamp},[]byte{})
	//计算出哈希地址
	hash:=sha256.Sum256(headers)
	block.Hash=hash[:]//设置哈希
}*/
//创建一个区块
func NewBlock(data string, prevBlockHash []byte) *Block{
	//block是一个指针,取得一个对象初始化之后的地址
	block:=&Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{},0}
	pow:=NewProofOfWork(block)//挖矿附加这个区块
	nonce,hash:=pow.Run()//开始挖矿
	block.Hash=hash[:]
	block.Nonce=nonce
	//block.SetHash()//设置当前哈希
	return block
}
//创建创世区块,
func NewGenesisBlock() *Block{
	return NewBlock("hello, welcome to my second BlockChain",[]byte{})

}
//blockchain.go
package main

type BlockChain struct{
	blocks []*Block    //一个数组,每个元素都是指针,存储block区块的地址
}
//增加一个区块
func (blocks *BlockChain)AddBlock(data string ){
	prevBlock:=blocks.blocks[len(blocks.blocks)-1]    //取出最后一个区块
	newBlock:=NewBlock(data,prevBlock.Hash)       //创建一个区块
	blocks.blocks=append(blocks.blocks,newBlock)    //区块链插入新的区块
}
//创建一个区块链
func NewBlockchain ()*BlockChain{
	return &BlockChain{[]*Block{NewGenesisBlock()}}
}
//proofofwork.go
package main

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math"
	"math/big"
)

var(
	maxNonce=math.MaxInt64 //最大的64位整数
)
const targetBits=24//对比的位数

type ProofOfWork struct{
	block *Block  //区块
	target * big.Int  //存储计算哈希对比的特定整数
}

//创建一个工作量证明的挖矿对象
func NewProofOfWork(block *Block)*ProofOfWork{
	target:=big.NewInt(1)     //初始化目标整数
	target.Lsh(target,uint(256-targetBits))  //数据转换
	pow:=&ProofOfWork{block,target}  //创建对象
	return pow
}
//准备数据进行挖矿计算
func (pow * ProofOfWork) prepareData(nonce int)[]byte{
	data:=bytes.Join(
		[][]byte{
			pow.block.PrevBlockHash,//上一块哈希
			pow.block.Data,//当前数据
			IntToHex(pow.block.Timestamp),//时间十六进制
			IntToHex(int64(targetBits)),//位数十六进制
			IntToHex(int64(nonce)),//保存工作量的nonce
		},[]byte{},
		)
	return data
}
//挖矿执行
func (pow * ProofOfWork) Run()(int,[]byte){
	var hashInt big.Int
	var hash [32]byte
	nonce:=0
	fmt.Printf("当前挖矿计算的区块数据%s",pow.block.Data)
	for nonce<maxNonce{
		data:=pow.prepareData(nonce)//准备好的数据
		hash=sha256.Sum256(data)//计算出哈希
		fmt.Printf("\r%x",hash)//打印显示哈希
		hashInt.SetBytes(hash[:])//获取要对比的数据
		if hashInt.Cmp(pow.target)==-1{//挖矿的校验
			break
		}else{
			nonce++
		}
	}
	fmt.Println("\n\n")
	return nonce,hash[:]//nonce解题的答案,hash当前哈希
}
//校验挖矿是不是真的成功
func (pow * ProofOfWork) Validate()bool{
	var hashInt big.Int
	data := pow.prepareData(pow.block.Nonce)//准备好的数据
	hash:=sha256.Sum256(data)//计算出哈希
	hashInt.SetBytes(hash[:])//获取要对比的数据
	isValid:=(hashInt.Cmp(pow.target)==-1)//校验数据
	return isValid
}
//test_pow.go
package main

import (
	"crypto/sha256"
	"fmt"
	"strconv"
	"time"
)

func mainx(){
	flag:=0
	start:=time.Now()   //当前时间
	for i:=0;i<10000000000;i++{   //循环挖矿
		data:=sha256.Sum256([]byte(strconv.Itoa(i)))   //计算哈希
		fmt.Printf("%10d,%x\n",i,data)
		fmt.Printf("%s\n",string(data[len(data)-2:]))
		if string(data[len(data)-2:])=="00"{  //位数的哈希匹配
			usedtime:=time.Since(start)
			fmt.Printf("挖矿成功,用时%d ms\n",usedtime)
			flag=1
			break
		}
	}
	if flag==0{
		println("挖矿失败")
	}
}

//main.go
package main

import (
	"fmt"
	"strconv"
)

func main(){
	fmt.Println("hello game start")
	bc:=NewBlockchain()   //创建区块链
	bc.AddBlock("小明1 pay 小红 10")
	bc.AddBlock("小明2 pay 小红 20")
	bc.AddBlock("小明3 pay 小红 30")

	for _,block:=range bc.blocks{
		fmt.Printf("上一块哈希%x\n",block.PrevBlockHash)
		fmt.Printf("数据: %s\n",block.Data)
		fmt.Printf("当前哈希%x\n",block.Hash)
		pow:=NewProofOfWork(block)//校验工作量
		fmt.Printf("pow %s\n",strconv.FormatBool(pow.Validate()))
		fmt.Println()
	}


}

 

10-04 16:23