经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
从第一行代码开始开发区块链(三)
来源:cnblogs  作者:通证派大本营  时间:2019/3/8 8:43:34  对本文有异议


传送门: 柏链项目学院



3. 增加数据库和客户端

之前我们的区块链已经产生了,但是程序终止后,数据就丢失了!为了下次启动后能够继续,我们需要引入一个小型数据库。这一次,我们选择与短跑世界记录保持者同名的数据库-bolt。

bolt的内容不详细在这里介绍了,大家可以参考文章:与短跑名将同名的数据库Bolt

定义数据库文件和bucket

  1. const dbFile = "blockchain.db"
  2. const blocksBucket = "blocks"

重新定义blockchain

  1. // Blockchain keeps a sequence of Blocks
  2. type Blockchain struct {
  3. tip []byte
  4. db *bolt.DB
  5. }

定义blockchain的迭代器

  1. // BlockchainIterator is used to iterate over blockchain blocks
  2. type BlockchainIterator struct {
  3. currentHash []byte
  4. db *bolt.DB
  5. }

处理创世块,使用boltdb

  1. func NewBlockchain() *Blockchain {
  2. var tip []byte
  3. db, err := bolt.Open(dbFile, 0600, nil)
  4. if err != nil {
  5. log.Panic(err)
  6. }
  7. err = db.Update(func(tx *bolt.Tx) error {
  8. b := tx.Bucket([]byte(blocksBucket))
  9. if b == nil {
  10. fmt.Println("No existing blockchain found. Creating a new one...")
  11. genesis := NewGenesisBlock()
  12. b, err := tx.CreateBucket([]byte(blocksBucket))
  13. if err != nil {
  14. log.Panic(err)
  15. }
  16. err = b.Put(genesis.Hash, genesis.Serialize())
  17. if err != nil {
  18. log.Panic(err)
  19. }
  20. err = b.Put([]byte("l"), genesis.Hash)
  21. if err != nil {
  22. log.Panic(err)
  23. }
  24. tip = genesis.Hash
  25. } else {
  26. tip = b.Get([]byte("l"))
  27. }
  28. return nil
  29. })
  30. if err != nil {
  31. log.Panic(err)
  32. }
  33. bc := Blockchain{tip, db}
  34. return &bc
  35. }

上述代码将创世块的数据写入到数据库中,并将创世块返回,注意此块的生成仍然需要pow工作量证明才可以。

接下来我们来写一个客户端,创建一个cli.go文件

cli肯定要记录区块链的数据

  1. type CLI struct {
  2. bc *Blockchain
  3. }

提供一个命令行的启动帮助

  1. func (cli *CLI) printUsage() {
  2. fmt.Println("Usage:")
  3. fmt.Println(" addblock -data BLOCK_DATA - add a block to the blockchain")
  4. fmt.Println(" printchain - print all the blocks of the blockchain")
  5. }

校验一下参数是否正确

  1. func (cli *CLI) validateArgs() {
  2. if len(os.Args) < 2 {
  3. cli.printUsage()
  4. os.Exit(1)
  5. }
  6. }

需要将之前blockchain添加一个AddBlock函数,需要将前一块的hash取出来

  1. // AddBlock saves provided data as a block in the blockchain
  2. func (bc *Blockchain) AddBlock(data string) {
  3. var lastHash []byte
  4. err := bc.db.View(func(tx *bolt.Tx) error {
  5. b := tx.Bucket([]byte(blocksBucket))
  6. lastHash = b.Get([]byte("l"))
  7. return nil
  8. })
  9. if err != nil {
  10. log.Panic(err)
  11. }
  12. newBlock := NewBlock(data, lastHash)
  13. err = bc.db.Update(func(tx *bolt.Tx) error {
  14. b := tx.Bucket([]byte(blocksBucket))
  15. err := b.Put(newBlock.Hash, newBlock.Serialize())
  16. if err != nil {
  17. log.Panic(err)
  18. }
  19. err = b.Put([]byte("l"), newBlock.Hash)
  20. if err != nil {
  21. log.Panic(err)
  22. }
  23. bc.tip = newBlock.Hash
  24. return nil
  25. })
  26. }

打印一下区块链信息

  1. func (cli *CLI) printChain() {
  2. bci := cli.bc.Iterator()
  3. for {
  4. block := bci.Next()
  5. fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
  6. fmt.Printf("Data: %s\n", block.Data)
  7. fmt.Printf("Hash: %x\n", block.Hash)
  8. pow := NewProofOfWork(block)
  9. fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
  10. fmt.Println()
  11. if len(block.PrevBlockHash) == 0 {
  12. break
  13. }
  14. }
  15. }

上述代码需要区块链迭代器支持next函数以可以遍历

  1. // Next returns next block starting from the tip
  2. func (i *BlockchainIterator) Next() *Block {
  3. var block *Block
  4. err := i.db.View(func(tx *bolt.Tx) error {
  5. b := tx.Bucket([]byte(blocksBucket))
  6. encodedBlock := b.Get(i.currentHash)
  7. block = DeserializeBlock(encodedBlock)
  8. return nil
  9. })
  10. if err != nil {
  11. log.Panic(err)
  12. }
  13. i.currentHash = block.PrevBlockHash
  14. return block
  15. }

DeserializeBlock是对区块链的数据进行读取和解码

  1. // DeserializeBlock deserializes a block
  2. func DeserializeBlock(d []byte) *Block {
  3. var block Block
  4. decoder := gob.NewDecoder(bytes.NewReader(d))
  5. err := decoder.Decode(&block)
  6. if err != nil {
  7. log.Panic(err)
  8. }
  9. return &block
  10. }

接下来核心部分,为cli增加一个运行函数

  1. // Run parses command line arguments and processes commands
  2. func (cli *CLI) Run() {
  3. cli.validateArgs()
  4. addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError)
  5. printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
  6. addBlockData := addBlockCmd.String("data", "", "Block data")
  7. switch os.Args[1] {
  8. case "addblock":
  9. err := addBlockCmd.Parse(os.Args[2:])
  10. if err != nil {
  11. log.Panic(err)
  12. }
  13. case "printchain":
  14. err := printChainCmd.Parse(os.Args[2:])
  15. if err != nil {
  16. log.Panic(err)
  17. }
  18. default:
  19. cli.printUsage()
  20. os.Exit(1)
  21. }
  22. if addBlockCmd.Parsed() {
  23. if *addBlockData == "" {
  24. addBlockCmd.Usage()
  25. os.Exit(1)
  26. }
  27. cli.addBlock(*addBlockData)
  28. }
  29. if printChainCmd.Parsed() {
  30. cli.printChain()
  31. }
  32. }

根据命令行输入的不同,执行不同的功能。



原文链接:http://www.cnblogs.com/tokenpai/p/10491305.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号