代码结构:
./coin/main.go
package main
import (
"core"
"fmt"
"strconv"
)
func main(){
bc:= core.NewBlockchain()
bc.AddBlock("Send 1 BTC to Ivan")
bc.AddBlock("Send 2 more BTC to Ivan")
for _,block :=range bc.Blocks{
fmt.Printf("Prev. hash:%x\n",block.PrevBlockHash)
fmt.Printf("Data: %s\n",block.Data)
fmt.Printf("Hash: %x\n",block.Hash)
pow:=core.NewProofOfWork(block)
fmt.Printf("PoW: %s\n",strconv.FormatBool(pow.Validate()))
fmt.Println()
}
}
./core/block.go
package core
import "time"
type Block struct{
Timestamp int64 // 区块链创建时间戳
Data []byte //区块包含的数据
PrevBlockHash []byte //前一个区块的哈希值
Hash []byte //区块自身的哈希值,用于校验区块数据有效
Nonce int //用于证明工作量
}
func NewBlock(data string,prevBlockHash []byte) *Block{
//block := &Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{}}
block := &Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{},0}
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
block.Hash = hash[:]
block.Nonce = nonce
return block
}
func NewGenesisBlock() *Block{
return NewBlock("Genesis Block",[]byte{})
}
.core/blockchain.go
package core
type Blockchain struct{
Blocks []*Block
}
func (bc *Blockchain)AddBlock(data string){
prevBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := NewBlock(data,prevBlock.Hash)
bc.Blocks = append(bc.Blocks,newBlock)
}
func NewBlockchain() *Blockchain{
return &Blockchain{[] *Block{NewGenesisBlock()}}
}
.core/proofofwork.go
package core
import (
"bytes"
"crypto/sha256"
"fmt"
"math"
"math/big"
)
var (
maxNonce = math.MaxInt64
)
const targetBits = 20
type ProofOfWork struct{
block *Block
target *big.Int
}
//新建一个NewProofOfWork的函数,并返回一个ProofOfWork
func NewProofOfWork(b *Block) *ProofOfWork{
target := big.NewInt(1)
target.Lsh(target,uint(256-targetBits)) //对比特位进行移位操作
pow:= &ProofOfWork{b,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)),
},
[]byte{},
)
return data
}
//运行一个pow
func (pow *ProofOfWork) Run() (int,[]byte){
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("Mining the block containing \"%s\"\n",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.Print("\n\n")
return 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
}
.core/utils.go
package core
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"log"
)
func IntToHex(num int64) []byte {
buff := new(bytes.Buffer)
err := binary.Write(buff,binary.BigEndian,num)
if err !=nil{
log.Panic(err)
}
return buff.Bytes()
}
func DataToHash(data []byte) []byte {
hash :=sha256.Sum256(data)
return hash[:]
}
这节课通过输入的语句和设置好的targetbits以及用于证明工作量的nonces来改变生成的Hash字符串。如果targetbits为12,字符串前面就有3个0,如果targetbits为20前面就有5个0。当nounce仅仅相差1时生成的字符串也是不同的。
而哈希校验则是在已知nonces情况下重新带入算法校验,如果结果和已知字符串是相同的,则证明字符串并非伪造。