bolt数据存储以及实现命令输入
main.go
package main
import "core"
func main() {
bc := core.NewBlockChain()
defer bc.Db.Close()
cli := core.CLI{bc}
cli.Run()
}
引入"github.com/boltdb/bolt"
blockchain.go
package core
import (
"github.com/boltdb/bolt"
"log"
"fmt"
)
const dbFile = "blockchain.db"
const blockBucket = "blocks"
//BlockChain keeps a sequence of Blocks
type BlockChain struct {
tip []byte
Db *bolt.DB
}
//BlockChainIterator is used to iterator over blockchain blocks
type BlockChainIterator struct {
currentHash []byte
db *bolt.DB
}
//AddBlock save provided data as a block in the blockchain
func (bc *BlockChain)AddBlock(data string) {
var lastHash []byte
err := bc.Db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
lastHash = b.Get([]byte("l"))
return nil
})
if err != nil {
log.Panic(err)
}
newBlock := NewBlock(data,lastHash)
err = bc.Db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
err := b.Put(newBlock.Hash, newBlock.Serialize())
if err != nil {
log.Panic(err)
}
err = b.Put([]byte("l"), newBlock.Hash)
if err != nil {
log.Panic(err)
}
bc.tip = newBlock.Hash
return nil
})
}
//Iterator
func (bc *BlockChain) Iterator() *BlockChainIterator {
bci := &BlockChainIterator{bc.tip,bc.Db}
return bci
}
//Next returns next block starting from the tip
func (i *BlockChainIterator) Next() *Block {
var block *Block
err := i.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
encoderBlock := b.Get(i.currentHash)
block = DeserializeBlock(encoderBlock)
return nil
})
if err != nil {
log.Panic(err)
}
i.currentHash = block.PrevBlockHash
return block
}
//NewBlockChain create a new BlockChain with genesis 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 {
b := tx.Bucket([]byte(blockBucket))
if b == nil {
fmt.Println("No existing blockchain found.Creating a new one...")
genesis := NewGenesisBlock()
b,err := tx.CreateBucket([]byte(blockBucket))
if err != nil {
log.Panic(err)
}
err = b.Put(genesis.Hash, genesis.Serialize())
if err != nil {
log.Panic(err)
}
err = b.Put([]byte("l"), genesis.Hash)
if err != nil {
log.Panic(err)
}
tip = genesis.Hash
}else{
tip = b.Get([]byte("l"))
}
return nil
})
if err != nil {
log.Panic(err)
}
bc := &BlockChain{Db:db, tip:tip}
return bc
}
序列化block与反序列
block.go
package core
import (
"time"
"strconv"
"bytes"
"crypto/sha256"
"encoding/gob"
"log"
)
//Block keeps block header
type Block struct {
Timestamp int64 //区块创建的时间
Data []byte //区块包含的数据
PrevBlockHash []byte //前一个区块的哈希值
Hash []byte //区块自身的哈希值,用于校验区块数据有效
Nonce int //记录工作量证明用到的数字
}
func (b *Block) Serialize() []byte {
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err := encoder.Encode(b)
if err != nil {
log.Panic(err)
}
return result.Bytes()
}
func DeserializeBlock(d []byte) *Block {
var block Block
decoder := gob.NewDecoder(bytes.NewReader(d))
err := decoder.Decode(&block)
if err != nil {
log.Panic(err)
}
return &block
}
//NewBlock create and returns Block
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{
Timestamp:time.Now().Unix(),
Data: []byte(data),
PrevBlockHash: prevBlockHash,
Hash: []byte{},
}
pow := NewProofOfWork(block) //新建工作量证明
nonce,hash := pow.Run() //执行工作量证明(挖矿)
block.Hash = hash
block.Nonce = nonce
return block
}
func (b *Block) SetHash() {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp},[]byte{})
hash := sha256.Sum256(headers)
b.Hash = hash[:]
}
//NewGenesisBlock create and returns genesis Block
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block", []byte{})
}
命令输入 flag
package core
import (
"fmt"
"os"
"flag"
"log"
"strconv"
)
type CLI struct {
Bc *BlockChain
}
func (cli *CLI) printUsage() {
fmt.Println("Usage:")
fmt.Println(" addblock -data BLOCK_DATA - add a block to the blockchain")
fmt.Println(" printchain - print all the blocks of the blockchain")
}
func (cli *CLI) validateArgs() {
if len(os.Args) < 2 {
cli.printUsage()
os.Exit(1)
}
}
func (cli *CLI) addBlock(data string) {
cli.Bc.AddBlock(data)
fmt.Println("Sucess!")
}
func (cli *CLI) printChain() {
bci := cli.Bc.Iterator()
for {
block := bci.Next()
fmt.Printf("Prev hash: %x\n", block.PrevBlockHash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Hash: %x\n", block.Hash)
//创建工作量证明
pow := NewProofOfWork(block)
//验证工作量证明
fmt.Printf("Pow: %s\n", strconv.FormatBool(pow.Validate()))
fmt.Println()
if len(block.PrevBlockHash) == 0 {
break
}
}
}
//Run parses command line arguments and process commands
func (cli *CLI) Run() {
cli.validateArgs()
addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError)
printChainCmd := flag.NewFlagSet("printchain", 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 "printchain":
err := printChainCmd.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)
}
cli.addBlock(*addBlockData)
}
if printChainCmd.Parsed() {
cli.printChain()
}
}

浙公网安备 33010602011771号