GO实现BTC-V2-挖矿
v2版本思路
1. POW介绍
– 定义一个工作量证明的结构ProofOfWork
a. block
b. 目标值
2. 提供创建POW的函数
– NewProofOfWork(参数)
3. 提供计算不断计算hash的哈数
– Run()
4. 提供一个校验函数
– IsValid()
POW定义
type ProofOfWork struct { block *Block //来存储哈希值,它内置一些方法Cmp:比较方法 // SetBytes : 把bytes转成big.int类型 []byte("0x00000919011eeb8fbdf0c476d8510b8e1e632eba7b584ac04c11ad20cbbdd394") // SetString : 把string转成big.int类型 "0x00000919011eeb8fbdf0c476d8510b8e1e632eba7b584ac04c11ad20cbbdd394" target *big.Int //系统提供的,是固定的 } func NewProofOfWork(block *Block) *ProofOfWork { pow := ProofOfWork{ block: block, } //写难度值,难度值应该是推导出来的,但是我们为了简化,把难度值先写成固定的,一切完成之后,再去推导 // 0000100000000000000000000000000000000000000000000000000000000000 //16制格式的字符串 targetStr := "0000100000000000000000000000000000000000000000000000000000000000" var bigIntTmp big.Int bigIntTmp.SetString(targetStr, 16) pow.target = &bigIntTmp return &pow }
RUN函数实现
//这是pow的运算函数,为了获取挖矿的随机数,同时返回区块的哈希值 func (pow *ProofOfWork) Run() ([]byte, uint64) { //1. 获取block数据 //2. 拼接nonce //3. sha256 //4. 与难度值比较 //a. 哈希值大于难度值,nonce++ //b. 哈希值小于难度值,挖矿成功,退出 var nonce uint64 //block := pow.block var hash [32]byte for ; ; { //data := block + nonce hash = sha256.Sum256(pow.prepareData(nonce)) //将hash(数组类型)转成big.int, 然后与pow.target进行比较, 需要引入局部变量 var bigIntTmp big.Int bigIntTmp.SetBytes(hash[:]) // -1 if x < y // 0 if x == y // +1 if x > y // //func (x *Int) Cmp(y *Int) (r int) { // x y if bigIntTmp.Cmp(pow.target) == -1 { //此时x < y , 挖矿成功! fmt.Printf("挖矿成功!nonce: %d, 哈希值为: %x\n", nonce, hash) break } else { nonce ++ } } return hash[:], nonce } func (pow *ProofOfWork) prepareData(nonce uint64) []byte { block := pow.block tmp := [][]byte{ uintToByte(block.Version), block.PrevBlockHash, block.MerKleRoot, uintToByte(block.TimeStamp), uintToByte(block.Difficulity), block.Data, uintToByte(nonce), } data := bytes.Join(tmp, []byte{}) return data }
使用pow更新NewBlock
//创建区块,对Block的每一个字段填充数据即可 func NewBlock(data string, prevBlockHash []byte) *Block { block := Block{ Version: 00, PrevBlockHash: prevBlockHash, MerKleRoot: []byte{}, TimeStamp: uint64(time.Now().Unix()), Difficulity: 10, //随便写的,v2再调整 Nonce: 10, //同Difficulty Data: []byte(data), Hash: []byte{}, //先填充为空,后续会填充数据 } //block.SetHash() pow := NewProofOfWork(&block) hash, nonce := pow.Run() block.Hash = hash block.Nonce = nonce return &block }
校验挖矿是否有效
func (pow *ProofOfWork) IsValid() bool { //在校验的时候,block的数据是完整的,我们要做的是校验一下,Hash,block数据,和Nonce是否满足难度值要求 //获取block数据 //拼接nonce //做sha256 //比较 //block := pow.block data := pow.prepareData(pow.block.Nonce) hash := sha256.Sum256(data) var tmp big.Int tmp.SetBytes(hash[:]) //if tmp.Cmp(pow.target) == -1 { // return true //} // return false return tmp.Cmp(pow.target) == -1 }
打印block字段
for i, block := range bc.Blocks { fmt.Printf("+++++++++++++++ %d ++++++++++++++\n", i) fmt.Printf("Version : %d\n", block.Version) fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash) fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot) timeFormat := time.Unix(int64(block.TimeStamp), 0).Format("2006-01-02 15:04:05") fmt.Printf("TimeStamp : %s\n", timeFormat) fmt.Printf("Difficulity : %d\n", block.Difficulity) fmt.Printf("Nonce : %d\n", block.Nonce) fmt.Printf("Hash : %x\n", block.Hash) fmt.Printf("Data : %s\n", block.Data) pow := NewProofOfWork(block) fmt.Printf("IsValid: %v\n", pow.IsValid()) }
使用Bits推导难度值
const Bits = 20 func NewProofOfWork(block *Block) *ProofOfWork { pow := ProofOfWork{ block: block, } //写难度值,难度值应该是推导出来的,但是我们为了简化,把难度值先写成固定的,一切完成之后,再去推导 // 0000100000000000000000000000000000000000000000000000000000000000 //固定难度值 //16制格式的字符串 //targetStr := "0001000000000000000000000000000000000000000000000000000000000000" //var bigIntTmp big.Int //bigIntTmp.SetString(targetStr, 16) // //pow.target = &bigIntTmp //程序推导难度值, 推导前导为3个难度值 // 0001000000000000000000000000000000000000000000000000000000000000 //初始化 // 0000000000000000000000000000000000000000000000000000000000000001 //向左移动, 256位 //1 0000000000000000000000000000000000000000000000000000000000000000 //向右移动, 四次,一个16进制位代表4个2进制(f:1111) //向右移动16位 //0 0001000000000000000000000000000000000000000000000000000000000000 bigIntTmp := big.NewInt(1) //bigIntTmp.Lsh(bigIntTmp, 256) //bigIntTmp.Rsh(bigIntTmp, 16) bigIntTmp.Lsh(bigIntTmp, 256 - Bits) pow.target = bigIntTmp return &pow }