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() } }