一、blot数据库
1.1 安装Boltdb数据库
go get github.com/boltdb/bolt
C:\Users\Administrator\go\src\github.com\boltdb\bolt
1.2 存储数据
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
func main(){
db, err := bolt.Open("BitCoin.db",0600,nil)
if err != nil {
log.Panicln("打开数据库出错")
}
updateErr := db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BitCoin"))
if bucket == nil{
fmt.Println("当前桶不存在")
bucket, err = tx.CreateBucket([]byte("BitCoin"))
if err != nil {
log.Panicln("创建桶失败")
}
}
bucket.Put([]byte("name"), []byte("jack"))
bucket.Put([]byte("age"), []byte("12"))
bucket.Put([]byte("address"), []byte("西安"))
return nil
})
if updateErr != nil{
log.Panicln("往数据库存储数据失败")
}
}
1.3 读取数据
db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BitCoin"))
if bucket == nil{
log.Panicln("查找桶失败")
}
name := string(bucket.Get([]byte("name")))
age := string(bucket.Get([]byte("age")))
address := string(bucket.Get([]byte("address")))
fmt.Println("name:",name, "age:",age, "address:",address)
return nil
})
二、基于bolt数据库实现简单的区块链
2.1区块链结构体
- 替换原来的指针数组
- Block的hash作为key
- Block节点的二进制流作为value
- 需要一个常量记录最后一个节点的hash值,不然无法创建新节点时记录上一节点的哈希值
type BlockChain struct {
db *bolt.DB
lastBlockHash []byte
}
2.2 创建带有创世块的区块链
func CreateBlockChain() *BlockChain{
var blockChain *BlockChain
genesisBlock := GenesisBlock()
db, err := bolt.Open("BlockCoin", 0600, nil)
if err != nil{
log.Panicln("打开数据库出错")
}
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil{
log.Panicln("创建bucket出错")
}
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
lashBlockHash = genesisBlock.Hash
}else {
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
lashBlockHash = genesisBlock.Hash
}
blockChain.db = db
blockChain.lastBlockHash = lashBlockHash
return nil
})
return blockChain
}
三、gob包
3.1 介绍
golang自带的一个数据结构编码/解码的工具。
3.2 编码解码实例
type Teacher struct {
Name string
Age int
Address string
}
func main(){
var mrsLi Teacher = Teacher{
Name: "jack",
Age: 59,
Address: "xian",
}
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(&mrsLi)
if err != nil{
fmt.Println("编码失败")
}
fmt.Println("编码后的李老师:",buffer.Bytes())
decoder := gob.NewDecoder(bytes.NewReader(buffer.Bytes()))
var mrsWu Teacher
err1 := decoder.Decode(&mrsWu)
if err1 != nil{
fmt.Println("解码出错")
}
fmt.Println(mrsWu.Age,mrsWu.Address,mrsWu.Name)
}
3.3 block文件
package main
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"log"
"time"
)
type Block struct{
Version uint64
PreBlockHash []byte
MerkelRoot []byte
TimeStamp uint64
Difficulty uint64
Nonce uint64
Hash []byte
Data []byte
}
func Uint64ConvertByte(data uint64)[]byte{
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian,data)
if err != nil {
log.Panicln(err)
}
return buffer.Bytes()
}
func CreateBlock(preBlockHash []byte, data string) *Block{
block := Block{
Version: 00,
PreBlockHash: preBlockHash,
MerkelRoot: []byte{},
TimeStamp: uint64(time.Now().Unix()),
Difficulty: 0,
Nonce: 0,
Data: []byte(data),
Hash: []byte{},
}
pow := CreatePOW(&block)
hash,nonce := pow.Run()
block.Hash = hash
block.Nonce = nonce
return &block
}
func GenesisBlock() *Block{
genesisBlock := CreateBlock([]byte{},"第一个创世块,牛逼")
return genesisBlock
}
func (block *Block)SetHash(){
var blockInfo []byte
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(block.Nonce),
block.Data,
}
bytes.Join(tem, []byte{})
Hash := sha256.Sum256(blockInfo)
block.Hash = Hash[:]
}
func (block *Block) blockToByte()[]byte{
return []byte{}
}
func (block *Block)Serialize() []byte{
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(block)
if err != nil{
fmt.Println("编码失败")
}
return buffer.Bytes()
}
func (block *Block) DeSerialize(buffer []byte) *Block{
decoder := gob.NewDecoder(bytes.NewReader(buffer))
err1 := decoder.Decode(block)
if err1 != nil{
fmt.Println("解码出错")
}
return block
}
3.4 blockChain文件
package main
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
type BlockChain struct {
db *bolt.DB
lastBlockHash []byte
}
var lashBlockHash []byte
func CreateBlockChain() *BlockChain{
var blockChain *BlockChain = &BlockChain{}
genesisBlock := GenesisBlock()
db, err := bolt.Open("BlockCoin", 0600, nil)
if err != nil{
log.Panicln("打开数据库出错")
}
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil{
log.Panicln("创建bucket出错")
}
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
lashBlockHash = genesisBlock.Hash
}else {
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
lashBlockHash = genesisBlock.Hash
}
blockChain.db = db
blockChain.lastBlockHash = lashBlockHash
return nil
})
return blockChain
}
func (blockChain *BlockChain)AddBlock(data string){
db := blockChain.db
lastBlockChain := blockChain.lastBlockHash
block := CreateBlock(lastBlockChain, data)
blockChain.lastBlockHash = block.Hash
fmt.Printf("当前区块的hash%d:\n",block.Hash)
fmt.Println("当前区块的数据:",string(block.Data))
fmt.Println("当前区块的随机值:",block.Nonce)
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil {
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil {
log.Panicln("创建bucket出错")
}
bucket.Put([]byte(block.Hash), block.Serialize())
} else {
bucket.Put([]byte(block.Hash), block.Serialize())
}
return nil
})
}
3.5 proofOfWork文件
package main
import (
"bytes"
"crypto/sha256"
"math/big"
)
type ProofOfWork struct {
block *Block
target *big.Int
}
func CreatePOW(block *Block) *ProofOfWork{
pow := ProofOfWork{
block: block,
}
target := "0000100000000000000000000000000000000000000000000000000000000000"
bigNum := big.Int{}
res, _ := bigNum.SetString(target, 16)
pow.target = res
return &pow
}
func (pow *ProofOfWork) Run()([]byte, uint64){
tmpBigInt := &big.Int{}
var nonce uint64 = 0
var hash [32]byte
for{
block := pow.block
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(nonce),
block.Data,
}
blockInfo := bytes.Join(tem, []byte(""))
hash = sha256.Sum256(blockInfo)
tmpBigInt.SetBytes(hash[:])
res := tmpBigInt.Cmp(pow.target)
if res == -1{
break
}
nonce ++
}
return hash[:],nonce
}
四、迭代器
4.1 迭代器的数据结构
type BlockChainIterator struct {
DB *bolt.DB
CurrentHashPtr []byte
}
4.2 创建迭代器函数
func (blockChain *BlockChain) NewBlockChainIterator() *BlockChainIterator{
blockChainIterator := BlockChainIterator{
DB: blockChain.db,
CurrentHashPtr: blockChain.lastBlockHash,
}
return &blockChainIterator
}
4.3 迭代函数
func (blockChainIterator *BlockChainIterator)nextBlock() *Block{
var block *Block
db := blockChainIterator.DB
db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
log.Panicln("迭代时出错")
}
currentHash := bucket.Get(blockChainIterator.CurrentHashPtr)
block = DeSerialize(currentHash)
blockChainIterator.CurrentHashPtr = block.PreBlockHash
return nil
})
return block
}
4.4 使用迭代器进行迭代
func main() {
firstBlockChain := CreateBlockChain()
firstBlockChain.AddBlock("jack向sheep转了50比特币")
firstBlockChain.AddBlock("jack向sheep转了80比特币")
iterator := firstBlockChain.NewBlockChainIterator()
for {
block := iterator.nextBlock()
fmt.Println("-------------------------------")
fmt.Printf("当前区块的hash:%x\n",block.Hash)
fmt.Printf("上一区块的hash:%x\n",block.PreBlockHash)
fmt.Printf("当前区块的数据:%s\n",string(block.Data))
if len(iterator.CurrentHashPtr) == 0{
fmt.Println("迭代结束")
break
}
}
}
4.5 结果
当前区块的hash[0 0 11 253 51 145 39 252 74 72 27 243 138 170 24 86 207 130 33 24 47 50 34 172 2 182 220 230 77 77 38 33]:
当前区块的数据: jack向sheep转了50比特币
当前区块的随机值: 1784998
当前区块的hash[0 0 0 30 7 7 134 66 43 201 134 147 176 39 52 67 42 89 53 28 162 81 219 229 62 109 158 31 180 75 191 110]:
当前区块的数据: jack向sheep转了80比特币
当前区块的随机值: 1897998
-------------------------------
当前区块的hash:0000001e070786422bc98693b02734432a59351ca251dbe53e6d9e1fb44bbf6e
上一区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
当前区块的数据:jack向sheep转了80比特币
-------------------------------
当前区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
上一区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
当前区块的数据:jack向sheep转了50比特币
-------------------------------
当前区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
上一区块的hash:
当前区块的数据:第一个创世块,牛逼
迭代结束
Process finished with the exit code 0
五、命令行
5.1 获取命令行
list := os.Args
5.2 通过命令行得到要运行的方法
- 比如go cmd addBlock --data “添加新的交易”,通过参数得知需要执行添加区块的功能。
type Cmd struct {
bChain *BlockChain
}
const Usage = `addBlock --data 添加区块
printBlock 输出区块`
func (cmd *Cmd) Run(){
list := os.Args
if len(list) < 2{
fmt.Println(Usage)
}else{
switch list[1] {
case "addBlock":
fmt.Println("添加区块")
fmt.Println(list[2], list[3])
case "printBlock":fmt.Println("输出区块")
default:
fmt.Println(Usage)
}
}
}
5.3 执行命令行
go build *.go
./cmd addBlock --data "添加一笔新的交易"
|