//block.go
package main
import (
"bytes"
"encoding/gob"
"time"
"log"
)
//定义区块
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{})
}
//对象转化为二进制字节集,可以写入文件
func (block *Block)Serialize() []byte{
var result bytes.Buffer //开辟内存,存放字节集合
encoder:=gob.NewEncoder(&result)//编码对象创建
err:=encoder.Encode(block)//编码操作
if err!=nil{
log.Panic(err)//处理错误
}
return result.Bytes()//返回字节
}
//读取文件,读到二进制字节集,二进制字节集和转化为对象
func DeserializeBlock(data []byte) *Block{
var block Block//对象存储用于字节转化的对象
decoder:=gob.NewDecoder(bytes.NewReader(data))//解码
err:=decoder.Decode(&block)//尝试解码
if err!=nil{
log.Panic(err)//处理错误
}
return &block
}
//blockchain.go
package main
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
const dbFile="blockchain.db" //数据库文件名当前目录下
const blockBucket="blocks" //名称
type BlockChain struct{
tip []byte //二进制数据
db *bolt.DB //数据库
}
type BlockChainIterator struct{
currentHash []byte //当前的哈希
db *bolt.DB //数据库
}
//增加一个区块
func (block *BlockChain)AddBlock(data string ){
var lastHash []byte //上一块哈希
err:=block.db.View(func(tx *bolt.Tx) error{
block:=tx.Bucket([]byte(blockBucket)) //取得数据
lastHash=block.Get([]byte("1")) //取得第一块
return nil
})
if err!=nil{
log.Panic(err)
}
newBlock:=NewBlock(data,lastHash)//创建一个新的区块
err=block.db.Update(func (tx *bolt.Tx)error{
bucket:=tx.Bucket([]byte(blockBucket))//取出
err:=bucket.Put(newBlock.Hash,newBlock.Serialize())//压入数据
if err!=nil{
log.Panic(err) //处理压入错误
}
err =bucket.Put([]byte("1"),newBlock.Hash)//压入数据
if err!=nil{
log.Panic(err) //处理压入错误
}
block.tip=newBlock.Hash //处理
return nil
})
}
//迭代器
func (block *BlockChain)Iterator()*BlockChainIterator{
bcit:=&BlockChainIterator{block.tip,block.db}
return bcit //根据区块链创建区块链迭代器
}
//取得下一个区块
func (it *BlockChainIterator)next()*Block{
var block *Block
err:=it.db.View(func (tx *bolt.Tx)error{
bucket:=tx.Bucket([]byte(blockBucket))
encodeBlock:=bucket.Get(it.currentHash)//抓取二进制数据
block=DeserializeBlock(encodeBlock)//解码
return nil
})
if err!=nil{
log.Panic(err)
}
it.currentHash=block.PrevBlockHash//哈希赋值
return block
}
//新建一个区块链
func NewBlockChain()*BlockChain{
var tip []byte //存储区块链的二进制数据
db,err:=bolt.Open(dbFile,0600,nil)//打开数据库
if err!=nil{
log.Panic(err)//处理数据库打开错误
}
//处理数据更新
err=db.Update(func (tx *bolt.Tx)error{
bucket:=tx.Bucket([]byte(blockBucket)) //按照名称打开数据库的表格
if bucket==nil{
fmt.Println("当前数据库没有区块链,没有创建一个新的")
genesis:=NewGenesisBlock()//创建创世区块
bucket,err:=tx.CreateBucket([]byte(blockBucket))//创建一个数据库表格
if err!=nil{
log.Panic(err) //处理创建错误
}
err=bucket.Put(genesis.Hash,genesis.Serialize())//存入数据
if err!=nil{
log.Panic(err) //处理存入错误
}
err=bucket.Put([]byte("1"),genesis.Hash)//存入数据
if err!=nil{
log.Panic(err) //处理存入错误
}
tip=genesis.Hash//取得哈希
}else{
tip=bucket.Get([]byte("1"))
}
return nil
})
if err!=nil{
log.Panic(err)//处理数据库更新错误
}
bc:=BlockChain{tip,db}//创建一个区块链
return &bc
}
//cli.go
package main
import (
"flag"
"fmt"
"github.com/labstack/gommon/log"
"os"
"strconv"
)
//命令行接口
type CLI struct{
blockchain *BlockChain
}
//用法
func (cli *CLI)printUsage(){
fmt.Println("用法如下")
fmt.Println("addblock 向区块链增加块")
fmt.Println("showchain 显示区块链")
}
func (cli *CLI)validateArgs() {
if len(os.Args)<2{
cli.printUsage()//显示用法
os.Exit(1)
}
}
func (cli *CLI)addBlock(data string){
cli.blockchain.AddBlock(data)//增加区块
fmt.Println("区块增加成功")
}
func (cli *CLI)showBlockChain(){
bci:=cli.blockchain.Iterator()//创建循环迭代器
for{
block:=bci.next()//取得下一个区块
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",strconv.FormatBool(pow.Validate()))
fmt.Println("\n")
if len(block.PrevBlockHash)==0{//遇到创世区块终止
break
}
}
}
func (cli *CLI)Run(){
cli.validateArgs()//校验
//处理命令行参数
addblockcmd:=flag.NewFlagSet("addblock",flag.ExitOnError)
showchaincmd:=flag.NewFlagSet("showchain",flag.ExitOnError)
addBlockData:=addblockcmd.String("data","","Block Data")
switch os.Args[1]{
case "addblock":
err:=addblockcmd.Parse(os.Args[2:])//解析参数
if err!=nil{
log.Panic(err)//处理错误
}
case "showchain":
err:=showchaincmd.Parse(os.Args[2:])//解析参数
if err!=nil{
log.Panic(err)//处理错误
}
default:
cli.printUsage()
os.Exit(1)
}
if addblockcmd.Parsed(){
if *addBlockData==""{
addblockcmd.Usage()
os.Exit(1)
}else{
cli.addBlock(*addBlockData)//增加区块
}
}
if showchaincmd.Parsed(){
cli.showBlockChain()//显示区块链
}
}
//proofofwork.go
package main
import (
"bytes"
"crypto/sha256"
"fmt"
"math"
"math/big"
)
var(
maxNonce=math.MaxInt64 //最大的64位整数
)
const targetBits=18//对比的位数
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
//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() //返回字节集合
}
//main.go
package main
func main(){
block:=NewBlockChain()//创建区块链
defer block.db.Close()//延迟关闭数据
cli:=CLI{block}//创建命令行
cli.Run()//开启
}