基于bolt資料庫實作簡單的區塊鏈 day(2)
- 一、blot資料庫
- 1.1 安裝Boltdb資料庫
- 1.2 存盤資料
- 1.3 讀取資料
- 二、基于bolt資料庫實作簡單的區塊鏈
- 2.1區塊鏈結構體
- 2.2 創建帶有創世塊的區塊鏈
- 三、gob包
- 3.1 介紹
- 3.2 編碼解碼實體
- 3.3 block檔案
- 3.4 blockChain檔案
- 3.5 proofOfWork檔案
- 四、迭代器
- 4.1 迭代器的資料結構
- 4.2 創建迭代器函式
- 4.3 迭代函式
- 4.4 使用迭代器進行迭代
- 4.5 結果
- 五、命令列
- 5.1 獲取命令列
- 5.2 通過命令列得到要運行的方法
- 5.3 執行命令列
一、blot資料庫
1.1 安裝Boltdb資料庫
- 他會下載到gopath下的路徑
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
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("西安"))
//回傳Nil,以便資料庫進行操作
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值,不然無法創建新節點時記錄上一節點的哈希值
// BlockChain 區塊鏈結構體
//將區塊鏈存盤在資料庫中
type BlockChain struct {
db *bolt.DB
//存盤最后一個區塊的hash
lastBlockHash []byte
}
2.2 創建帶有創世塊的區塊鏈
// CreateBlockChain 創建帶有一個創世塊的區塊鏈
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
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())
//存盤最后一個區塊的hash資訊
lashBlockHash = genesisBlock.Hash
}else {
//存盤創世塊的資訊
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
//存盤最后一個區塊的hash資訊
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",
}
//1.編碼的資料放到buffer中
var buffer bytes.Buffer
//2.定義一個編碼器
encoder := gob.NewEncoder(&buffer)
//3.使用編碼器進行編碼
err := encoder.Encode(&mrsLi)
if err != nil{
fmt.Println("編碼失敗")
}
fmt.Println("編碼后的李老師:",buffer.Bytes())
//1.定義一個解碼器
decoder := gob.NewDecoder(bytes.NewReader(buffer.Bytes()))
//2.定義解碼后承接的變數
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"
)
// Block 區塊結構體
type Block struct{
//1.區塊版本號
Version uint64
//2.前區塊Hash
PreBlockHash []byte
//3.merkel根
MerkelRoot []byte
//4.時間戳
TimeStamp uint64
//5.難度值
Difficulty uint64
//6.亂數,也就是挖礦要找的資料
Nonce uint64
//a.當前區塊的Hash值,在現實位元幣中沒有這個
Hash []byte
Data []byte
}
// Uint64ConvertByte 將uint64型別轉換為[]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()
}
// CreateBlock 創建block
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{},
}
//進行挖礦,得到挖礦成功后的hash和隨機值
pow := CreatePOW(&block)
//挖礦模擬,不斷改變亂數,計算hash,直到找到合適的hash
hash,nonce := pow.Run()
//根據挖礦結果對區塊資料不斷進行更新
block.Hash = hash
block.Nonce = nonce
//block.SetHash()
return &block
}
// GenesisBlock 創建創世塊,即第一個區塊
func GenesisBlock() *Block{
genesisBlock := CreateBlock([]byte{},"第一個創世塊,牛逼")
return genesisBlock
}
// SetHash 計算當前區塊的hash
func (block *Block)SetHash(){
var blockInfo []byte
//...的作用是把data陣列打散,一個個裝進preBlockHash切片中
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Version)...)
//blockInfo = append(blockInfo, block.PreBlockHash...)
//blockInfo = append(blockInfo, block.MerkelRoot...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.TimeStamp)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Difficulty)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Nonce)...)
//blockInfo = append(blockInfo, block.Data...)
// 創建一個二維陣列
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(block.Nonce),
block.Data,
}
//將二維byte陣列連接成一維byte陣列
bytes.Join(tem, []byte{})
//hash是一個位元組陣列
Hash := sha256.Sum256(blockInfo)
//block.Hash作為Hash的切片
block.Hash = Hash[:]
}
func (block *Block) blockToByte()[]byte{
return []byte{}
}
func (block *Block)Serialize() []byte{
//1.編碼的資料放到buffer中
var buffer bytes.Buffer
//2.定義一個編碼器
encoder := gob.NewEncoder(&buffer)
//3.使用編碼器進行編碼
err := encoder.Encode(block)
if err != nil{
fmt.Println("編碼失敗")
}
return buffer.Bytes()
}
func (block *Block) DeSerialize(buffer []byte) *Block{
//1.定義一個解碼器
decoder := gob.NewDecoder(bytes.NewReader(buffer))
//2.定義解碼后承接的變數
err1 := decoder.Decode(block)
if err1 != nil{
fmt.Println("解碼出錯")
}
return block
}
3.4 blockChain檔案
package main
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
// BlockChain 區塊鏈結構體
//將區塊鏈存盤在資料庫中
type BlockChain struct {
db *bolt.DB
//存盤最后一個區塊的hash
lastBlockHash []byte
}
var lashBlockHash []byte
// CreateBlockChain 創建帶有一個創世塊的區塊鏈
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
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())
//存盤最后一個區塊的hash資訊
lashBlockHash = genesisBlock.Hash
}else {
//存盤創世塊的資訊
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
//存盤最后一個區塊的hash資訊
lashBlockHash = genesisBlock.Hash
}
//試著輸出一下genesisBlock的資訊
//fmt.Println(genesisBlock.Serialize())
blockChain.db = db
blockChain.lastBlockHash = lashBlockHash
return nil
})
return blockChain
}
// AddBlock 當前區塊的前一區塊哈希值欄位從區塊鏈中獲取
func (blockChain *BlockChain)AddBlock(data string){
db := blockChain.db
lastBlockChain := blockChain.lastBlockHash
block := CreateBlock(lastBlockChain, data)
//更新區塊鏈條最后一個區塊的hash資訊
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
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"
)
// ProofOfWork 挖礦模塊
// ProofOfWork 作業量證明機制
type ProofOfWork struct {
block *Block
target *big.Int
}
//CreatePOW 創建ProofOfWork
func CreatePOW(block *Block) *ProofOfWork{
pow := ProofOfWork{
block: block,
}
target := "0000100000000000000000000000000000000000000000000000000000000000"
bigNum := big.Int{}
//res是指標型別的
res, _ := bigNum.SetString(target, 16)
pow.target = res
return &pow
}
//Run 回傳一個Hash值和亂數
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 拼裝好的資料
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
//當前hash的指標
CurrentHashPtr []byte
}
4.2 創建迭代器函式
- 迭代器函式是屬于區塊鏈的函式
func (blockChain *BlockChain) NewBlockChainIterator() *BlockChainIterator{
//如何判斷blockChain是否存在??
blockChainIterator := BlockChainIterator{
DB: blockChain.db,
CurrentHashPtr: blockChain.lastBlockHash,
}
return &blockChainIterator
}
4.3 迭代函式
- 迭代函式是屬于迭代器結構體
// nextBlock 進行迭代: 1.回傳一個Block 2.迭代指標往前移
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物件
block = DeSerialize(currentHash)
//指標往前移動
blockChainIterator.CurrentHashPtr = block.PreBlockHash
return nil
})
return block
}
4.4 使用迭代器進行迭代
func main() {
//創建第一個區塊鏈
firstBlockChain := CreateBlockChain()
//fmt.Println(firstBlockChain.lastBlockHash)
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])
//cmd.bChain.AddBlock()
case "printBlock":fmt.Println("輸出區塊")
default:
fmt.Println(Usage)
}
}
}
5.3 執行命令列
go build *.go
./cmd addBlock --data "添加一筆新的交易"
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/304540.html
標籤:區塊鏈
上一篇:隱私政策宣告
