目錄git
一. flag基本使用程序員
一般咱們在寫命令行程序(工具、server)時,對命令參數進行解析是常見的需求。各類語言通常都會提供解析命令行參數的方法或庫,以方便程序員使用。在 go 標準庫中提供了一個包:flag,方便進行命令行解析。github
1.導入flag包數據庫
import ( "flag" )
2.使用示例ide
//定義一個字符串flag,flag名爲printchain,默認值爲:hello BTC world,參數說明: 輸出全部的區塊信息 flagString := flag.String("printchain", "hello BTC world", "輸出全部的區塊信息") //定義一個整型flag,flag名爲:number ,默認值爲:6 ,參數說明:輸入一個整數 flagInt := flag.Int("number", 6, "輸出一個整數...") //定義一個布爾類型的flag,flag名爲:open,默認值:false,參數說明:判斷真假 flagBool := flag.Bool("open", false, "判斷真假...") //解析flag flag.Parse() //輸入參數後的值 fmt.Printf("%s\n", *flagString) fmt.Printf("%d\n", *flagInt) fmt.Printf("%t\n", *flagBool)
執行結果函數
//編譯main.go go build main.go
2.1 默認執行工具
./main //執行./main,輸出全部flag的默認值 hello BTC world 6 false
2.2 .輸入一個未定義的flag區塊鏈
./main -xx //xx並未定義,輸出錯誤信息以及全部flag使用方法(經過flag.Parse()生效) flag provided but not defined: -xx Usage of ./main: -number int 輸出一個整數... (default 6) -open 判斷真假... -printchain string 輸出全部的區塊信息 (default "hello BTC world")
2.3 輸入已定義的flag測試
./main -printchain //僅僅傳入flag,提示flag須要一個參數值 flag needs an argument: -printchain Usage of ./main: -number int 輸出一個整數... (default 6) -open 判斷真假... -printchain string 輸出全部的區塊信息 (default "hello BTC world")
2.4 輸入已定義的flag以及對應類型的值ui
./main -printchain "hello bruce" //傳入flag=printchain,value="hello bruce" hello bruce 6 false ./main -number 88 //傳入flag=number,value=88 hello BTC world 88 false ./main -open true //傳入flag=open,value=true (默認爲false,傳入-open即爲true) hello BTC world 6 true
二.os.Args基本使用
os包提供了一些與操做系統交互的函數和變量,而且go對其作了一些封裝。程序的命令行參數能夠從os包的Args變量獲取;os包外部使用os.Args訪問該變量。
Go言裏也採用左閉右開形式, 即,區間包括第一個索引元素,不包括最後一個, 由於這樣能夠簡化邏輯。os.Args的第一個元素,os.Args[0], 是命令自己的名字;其它的元素則是程序啓動時傳給它的參數。
1.導入os包
import os
使用示例
//實例化os.Args
args := os.Args
//打印args切片全部內容
fmt.Printf("%v\n",args)
//打印args切片的第二個參數
fmt.Printf("%v\n",args[1])
//打印args切片的第三個參數
fmt.Printf("%v\n",args[2])
傳入參數
./main arg1 arg2 //執行main程序,傳入兩個參數arg [./main arg1 arg2] //args arg1 //args[1] arg2 //args[2]
三.flag與os.Args組合使用
1.建立flag對象
//經過flag.NewFlagSet實例化flag對象 addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printChain", flag.ExitOnError)
2.拼接flag
flag名稱:data
默認值:bruce
使用說明:交易數據
使用方法: ./main addBlock -data
flagAddBlockData := addBlockCmd.String("data", "bruce", "交易數據")
3.判斷flag
//判斷os.Args中第二個flag的值 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: printUsage() os.Exit(1) }
4.解析flag輸出信息
if addBlockCmd.Parsed() { if *flagAddBlockData == "" { printUsage() os.Exit(1) } fmt.Println(*flagAddBlockData) } if printChainCmd.Parsed() { fmt.Println("測試輸出區塊信息...") }
5.相關函數
5.1 打印使用說明
func printUsage() { fmt.Println("Usage:") fmt.Println("\taddblock -data DATA -- 交易數據") fmt.Println("\tprintChain --輸出區塊信息") }
5.2 判斷是否輸入flag
func isValid() { //若是args切片長度小於2(即沒有跟參數,則打印使用方法並退出程序) if len(os.Args) < 2 { printUsage() os.Exit(1) } }
6.測試命令行
6.1 無參數
./main Usage: addBlock -data DATA -- 交易數據: printChain --輸出區塊信息
6.2 無效參數
./main xxx Usage: addBlock -data DATA -- 交易數據: printChain --輸出區塊信息
6.3 參數默認值
./main addBlock //輸出flag=data的默認值bruce bruce ./main printChain //輸出解析到printChain後打印的信息 測試輸出區塊信息...
6.4 指定參數
./main addBlock -data "brucefeng" //輸出flag=data的輸入值brucefeng brucefeng
四.經過命令行添加/查詢區塊
1.實現目標
Usage: createblockchain -data DATA --交易數據 addBlock -data DATA -- 交易數據 printChain --輸出區塊信息
2.定義命令行屬性與方法 CLI.go
2.1 導入相關包
import ( "fmt" "os" "flag" "log" )
2.2 定義結構體
type CLI struct{}
2.3 打印幫助提示
func printUsage() { fmt.Println("Usage:") fmt.Println("\tcreateblockchain -data DATA --交易數據") fmt.Println("\taddBlock -data DATA -- 交易數據") fmt.Println("\tprintChain --輸出區塊信息") }
2.4 判斷參數輸入是否合法
func isValid() { if len(os.Args) < 2 { printUsage() os.Exit(1) } }
2.5 定義創世區塊的方法
func (cli *CLI) createGenenisBlockChain(data string) { CreateBlockChainWithGenesisBlock(data) }
2.6 定義普通區塊的方法
func (cli *CLI) addBlock(data string) { //判斷數據庫是否存在 if !DBExists() { fmt.Println("當前不存在區塊鏈,請先建立創世區塊") os.Exit(1) } //經過BlockChainObject()函數獲取一個block blockchain := BlockChainObject() defer blockchain.DB.Close() //經過blockchain的AddBlockChain方法添加區塊 blockchain.AddBlockChain(data) }
2.7 定義遍歷區塊的方法
func (cli *CLI) printChain() { //判斷數據庫是否存在 if !DBExists() { fmt.Println("當前不存在區塊鏈") os.Exit(1) } blockchain := BlockChainObject() defer blockchain.DB.Close() blockchain.PrintChain() }
2.8 定義命令行參數方法集合
func (cli *CLI) Run() { //1.判斷命令行參數是否合法 isValid() //2.經過flag.NewFlagSet實例化三個flag對象 //建立創世區塊 createBlockCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) //建立普通區塊 addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) //遍歷打印區塊信息 printChainCmd := flag.NewFlagSet("printChain", flag.ExitOnError) //3.定義命令行對象的相關屬性 flagCreateBlockChainWithData := createBlockCmd.String("data", "", "交易數據") flagAddBlockData := addBlockCmd.String("data", "", "交易數據") //4.根據參數值決定進行解析的信息 switch os.Args[1] { case "createblockchain": err := createBlockCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } 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: printUsage() os.Exit(1) } //5. 判斷是否解析成功 Parsed reports whether f.Parse has been called. //5.1 若是createBlockCmd解析成功,輸出的DATA的值做爲建立創世區塊的傳入參數 if createBlockCmd.Parsed() { if *flagCreateBlockChainWithData == "" { fmt.Println("創世區塊交易數據不能爲空") printUsage() os.Exit(1) } //調用createGenenisBlockChain()方法建立創世區塊 cli.createGenenisBlockChain(*flatCreateBlockChainWithData) } //5.2 若是addBlockCmd解析成功,輸出的DATA的值做爲建立新區塊的傳入參數 if addBlockCmd.Parsed() { if *flagAddBlockData == "" { fmt.Println("建立區塊交易數據不能爲空") printUsage() os.Exit(1) } //調用addBlock方法建立區塊 cli.addBlock(*flagAddBlockData) } //5.3 若是printChainCmd解析成功,調用遍歷區塊鏈的方法 if printChainCmd.Parsed() { cli.printChain() } }
2.9 代碼整合
package BLC import ( "fmt" "os" "flag" "log" ) type CLI struct{} func printUsage() { fmt.Println("Usage:") fmt.Println("\tcreateblockchain -data DATA --交易數據") fmt.Println("\taddBlock -data DATA -- 交易數據") fmt.Println("\tprintChain --輸出區塊信息") } func isValid() { if len(os.Args) < 2 { printUsage() os.Exit(1) } } func (cli *CLI) addBlock(data string) { if !DBExists() { fmt.Println("數據庫不存在") os.Exit(1) } blockchain := BlockChainObject() defer blockchain.DB.Close() blockchain.AddBlockChain(data) } func (cli *CLI) printChain() { if !DBExists() { fmt.Println("數據庫不存在") os.Exit(1) } blockchain := BlockChainObject() defer blockchain.DB.Close() blockchain.PrintChain() } func (cli *CLI) createGenenisBlockChain(data string) { CreateBlockChainWithGenesisBlock(data) } func (cli *CLI) Run() { isValid() addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printChain", flag.ExitOnError) createBlockCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) flagAddBlockData := addBlockCmd.String("data", "", "交易數據") flagCreateBlockChainWithData := createBlockCmd.String("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) } case "createblockchain": err := createBlockCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } default: printUsage() os.Exit(1) } if addBlockCmd.Parsed() { if *flagAddBlockData == "" { printUsage() os.Exit(1) } cli.addBlock(*flagAddBlockData) } if printChainCmd.Parsed() { cli.printChain() } if createBlockCmd.Parsed() { if *flagCreateBlockChainWithData == "" { fmt.Println("交易數據不能爲空") printUsage() os.Exit(1) } cli.createGenenisBlockChain(*flagCreateBlockChainWithData) } }
3.生成區塊鏈方法改造 BlockChain.go
3.1 定義判斷數據庫是否存在的方法
func DBExists() bool { if _, err := os.Stat(dbName); os.IsNotExist(err) { return false } return true }
3.2 定義返回BlockChain對象
func BlockChainObject() *BlockChain { //定義tip用於存儲從數據庫中獲取到的最新區塊的Hash值 var tip []byte //打開數據庫 db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } //經過key="l"獲取value值(最新區塊的Hash值) err = db.View(func(tx *bolt.Tx) error { //獲取表對象 b := tx.Bucket([]byte(blockTableName)) if b != nil { //獲取最新區塊的Hash值 tip = b.Get([]byte("l")) } return nil }) //返回保存最新區塊Hash信息的BlockChain對象 return &BlockChain{tip, db} }
3.3 建立帶有創世區塊的區塊鏈
func CreateBlockChainWithGenesisBlock(data string) { //判斷數據庫是否存在 if DBExists() { fmt.Println("創世區塊已經存在") os.Exit(1) } //打開數據庫 db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } err = db.Update(func(tx *bolt.Tx) error { //建立數據庫表 b, err := tx.CreateBucket([]byte(blockTableName)) if err != nil { log.Panic(err) } if b != nil { //建立創世區塊 genesisBlock := CreateGenesisBlock(data) //將創世區塊存儲至表中 err := b.Put(genesisBlock.Hash, genesisBlock.Serialize()) if err != nil { log.Panic(err) } //存儲最新的區塊鏈的hash err = b.Put([]byte("l"), genesisBlock.Hash) if err != nil { log.Panic(err) } } return nil }) if err != nil { log.Fatal(err) } }
3.4 建立添加區塊的方法
func (blc *BlockChain) AddBlockChain(data string) { err := blc.DB.Update(func(tx *bolt.Tx) error { //1.獲取表 b := tx.Bucket([]byte(blockTableName)) //2.建立新區塊 if b != nil { //獲取最新區塊 byteBytes := b.Get(blc.Tip) //反序列化 block := DeserializeBlock(byteBytes) //3. 將區塊序列化而且存儲到數據庫中 newBlock := NewBlock(data, block.Height+1, block.Hash) err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } //4.更新數據庫中"l"對應的Hash err = b.Put([]byte("l"), newBlock.Hash) if err != nil { log.Panic(err) } //5. 更新blockchain的Tip blc.Tip = newBlock.Hash } return nil }) if err != nil { log.Panic(err) } }
3.5 遍歷區塊鏈的方法
func (blc *BlockChain) PrintChain() { blockchainIterator := blc.Iterator() for { block := blockchainIterator.Next() fmt.Printf("Height:%d\n", block.Height) fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash) fmt.Printf("Data:%s\n", block.Data) fmt.Printf("TimeStamp:%s\n", time.Unix(block.TimeStamp, 0).Format("2006-01-02 03:04:05 PM")) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("Nonce:%d\n", block.Nonce) var hashInt big.Int hashInt.SetBytes(block.PreBlockHash) if big.NewInt(0).Cmp(&hashInt) == 0 { break } } }
3.6 代碼整合
package BLC import ( "github.com/boltdb/bolt" "log" "math/big" "fmt" "time" "os" ) const dbName = "blockchain.db" //數據庫名 const blockTableName = "blocks" //表名 type BlockChain struct { Tip []byte //區塊鏈裏面最後一個區塊的Hash DB *bolt.DB //數據庫 } //迭代器 func (blockchain *BlockChain) Iterator() *BlockChainIterator { return &BlockChainIterator{blockchain.Tip, blockchain.DB} } //判斷數據庫是否存在 func DBExists() bool { if _, err := os.Stat(dbName); os.IsNotExist(err) { return false } return true } func (blc *BlockChain) PrintChain() { blockchainIterator := blc.Iterator() for { block := blockchainIterator.Next() fmt.Printf("Height:%d\n", block.Height) fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash) fmt.Printf("Data:%s\n", block.Data) fmt.Printf("TimeStamp:%s\n", time.Unix(block.TimeStamp, 0).Format("2006-01-02 03:04:05 PM")) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("Nonce:%d\n", block.Nonce) var hashInt big.Int hashInt.SetBytes(block.PreBlockHash) if big.NewInt(0).Cmp(&hashInt) == 0 { break } } } // func (blc *BlockChain) AddBlockChain(data string) { err := blc.DB.Update(func(tx *bolt.Tx) error { //1.獲取表 b := tx.Bucket([]byte(blockTableName)) //2.建立新區塊 if b != nil { //獲取最新區塊 byteBytes := b.Get(blc.Tip) //反序列化 block := DeserializeBlock(byteBytes) //3. 將區塊序列化而且存儲到數據庫中 newBlock := NewBlock(data, block.Height+1, block.Hash) err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } //4.更新數據庫中"l"對應的Hash err = b.Put([]byte("l"), newBlock.Hash) if err != nil { log.Panic(err) } //5. 更新blockchain的Tip blc.Tip = newBlock.Hash } return nil }) if err != nil { log.Panic(err) } } //1.建立帶有創世區塊的區塊鏈 func CreateBlockChainWithGenesisBlock(data string) { //判斷數據庫是否存在 if DBExists() { fmt.Println("創世區塊已經存在") os.Exit(1) } //打開數據庫 db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } err = db.Update(func(tx *bolt.Tx) error { //建立數據庫表 b, err := tx.CreateBucket([]byte(blockTableName)) if err != nil { log.Panic(err) } if b != nil { //建立創世區塊 genesisBlock := CreateGenesisBlock(data) //將創世區塊存儲至表中 err := b.Put(genesisBlock.Hash, genesisBlock.Serialize()) if err != nil { log.Panic(err) } //存儲最新的區塊鏈的hash err = b.Put([]byte("l"), genesisBlock.Hash) if err != nil { log.Panic(err) } } return nil }) if err != nil { log.Fatal(err) } } //返回BlockChain對象 func BlockChainObject() *BlockChain { var tip []byte //打開數據庫 db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } err = db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blockTableName)) if b != nil { //讀取最新區塊的Hash tip = b.Get([]byte("l")) } return nil }) return &BlockChain{tip, db} }
以上未改造代碼能夠參考:【Golang區塊鏈開發003】區塊序列化存儲
https://mp.weixin.qq.com/s?__biz=MzA4Mzg5NTEyOA==&mid=2651781600&idx=1&sn=ce72dd45079759dd004be83ed3f9f90c&chksm=84152fd7b362a6c18d1b752b8ffc14d07a057dd4f263c14c3058a822a7b74f7ae7bcb9eb1cd2#rd
五.測試代碼與測試結果
測試代碼main.go
func main() {
//初始化CLI對象
cli := BLC.CLI{}
fmt.Println("====返回結果======")
//執行Run命令調用對應參數對應的方法
cli.Run()
}
2.測試結果
2.1 無參數執行
./main //直接執行 ====返回結果====== Usage: createblockchain -data DATA --交易數據 addBlock -data DATA -- 交易數據 printChain --輸出區塊信息
2.2 首次遍歷區塊鏈
./main printChain ====返回結果====== 當前不存在區塊鏈
2.3 首次添加普通區塊
./main addBlock 或者 ./main addBlock -data "bruce" ====返回結果====== 當前不存在區塊鏈,請先建立創世區塊
2.4 建立創世區塊
./main createblockchain --data "Create Genenis Block 20180708" ====返回結果====== 第1個區塊,挖礦成功:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 2018-07-08 22:03:23.939771259 +0800 CST m=+0.087935424
2.5 遍歷區塊鏈
./main printChain ====返回結果====== Height:1 PreBlockHash:0000000000000000000000000000000000000000000000000000000000000000 Data:Create Genenis Block 20180708 TimeStamp:2018-07-08 10:03:23 PM Hash:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 Nonce:61212
2.6 添加區塊
./main addBlock -data "bruce" ====返回結果====== 第2個區塊,挖礦成功:000081872fe39a35be79e30f915c9716b9bbbae74f665fdd53f4ab57ae4b379d 2018-07-08 22:06:27.642330532 +0800 CST m=+0.055636890
2.7 遍歷區塊鏈
./main printChain ====返回結果====== Height:2 PreBlockHash:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 Data:bruce TimeStamp:2018-07-08 10:06:27 PM Hash:000081872fe39a35be79e30f915c9716b9bbbae74f665fdd53f4ab57ae4b379d Nonce:39906 Height:1 PreBlockHash:0000000000000000000000000000000000000000000000000000000000000000 Data:Create Genenis Block 20180708 TimeStamp:2018-07-08 10:03:23 PM Hash:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 Nonce:61212
2.8 添加多個區塊
./main addBlock -data "bruce" ./main addBlock -data "send 100 BTC TO bruce" ...... 第3個區塊,挖礦成功:0000565d60b6a26b8fc238bfeffb2cc1de20d28ca2a312bef9601997bb914fc3 2018-07-08 22:09:52.644353193 +0800 CST m=+0.032078853 第4個區塊,挖礦成功:0000dd1354cc868b96aeb18360b320b7a30ea0c00b27b79c32c0d9269ff2dbb3 2018-07-08 22:10:22.805477297 +0800 CST m=+0.210806453
2.9 遍歷整個區塊鏈
./main printChain ====返回結果====== Height:6 PreBlockHash:000019ba65a19b26f81fae22025e963e68a6cf0983fb211aae208c428368252d Data:bruce send 30 BTC TO jackma TimeStamp:2018-07-08 10:12:57 PM Hash:0000e3ba0e34ace19cba574deefdf5b75315b2ce10e488332952544e5a056571 Nonce:3502 Height:5 PreBlockHash:0000dd1354cc868b96aeb18360b320b7a30ea0c00b27b79c32c0d9269ff2dbb3 Data:bruce send 50 BTC TO ponyma TimeStamp:2018-07-08 10:12:44 PM Hash:000019ba65a19b26f81fae22025e963e68a6cf0983fb211aae208c428368252d Nonce:80833 Height:4 PreBlockHash:0000565d60b6a26b8fc238bfeffb2cc1de20d28ca2a312bef9601997bb914fc3 Data:send 100 BTC TO bruce TimeStamp:2018-07-08 10:10:22 PM Hash:0000dd1354cc868b96aeb18360b320b7a30ea0c00b27b79c32c0d9269ff2dbb3 Nonce:182779 Height:3 PreBlockHash:000081872fe39a35be79e30f915c9716b9bbbae74f665fdd53f4ab57ae4b379d Data:bruce TimeStamp:2018-07-08 10:09:52 PM Hash:0000565d60b6a26b8fc238bfeffb2cc1de20d28ca2a312bef9601997bb914fc3 Nonce:21138 Height:2 PreBlockHash:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 Data:bruce TimeStamp:2018-07-08 10:06:27 PM Hash:000081872fe39a35be79e30f915c9716b9bbbae74f665fdd53f4ab57ae4b379d Nonce:39906 Height:1 PreBlockHash:0000000000000000000000000000000000000000000000000000000000000000 Data:Create Genenis Block 20180708 TimeStamp:2018-07-08 10:03:23 PM Hash:0000d5b050b448b6db0d273b9320036f93b7cb2e8f8005c1e6192dc91b4a3381 Nonce:61212
2.10 測試從新建立區塊結構
./main createblockchain -data "第二次建立創世區塊" ====返回結果====== 創世區塊已經存在