2.4 介面

尹成發表於2018-11-08

CLI 命令列介面

直到現在,我們的實現還沒有提供任何操作介面給外界使用。我們先前的例子中在 main 函式中執行新建區塊鏈 NewBlockchain,還有新增區塊 bc.AddBlock 的方法。現在可以改善,增加命令列操作介面了。我們需要如下這樣的命令:

$ blockchain_go addblock “Pay 0.031337 for a coffee”

$ blockchain_go printchain

所有的命令列關聯的操作會被 CLI 結構處理:

type CLI struct {
        bc *Blockchain
}

在 Run中加入CLI的接入點:

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:])
        case "printchain":
                err := printChainCmd.Parse(os.Args[2:])
        default:
                cli.printUsage()
                os.Exit(1)
        }
if addBlockCmd.Parsed() {
                if *addBlockData == "" {
                        addBlockCmd.Usage()
                        os.Exit(1)
                }
                cli.addBlock(*addBlockData)
        }
if printChainCmd.Parsed() {
                cli.printChain()
        }
}

使用標準的 flag 解析這些引數

addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError)
printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
addBlockData := addBlockCmd.String("data", "", "Block data")

首先,建立兩個子命令, addblock 和 printfchain,用 -add 標記作為 addblock 的引數資料標識。printfchain 不用引數:

switch os.Args[1] {
case "addblock":
        err := addBlockCmd.Parse(os.Args[2:])
case "printchain":
        err := printChainCmd.Parse(os.Args[2:])
default:
        cli.printUsage()
        os.Exit(1)
}

檢測使用者輸入的引數和解析相關的 flag 子命令。

if addBlockCmd.Parsed() {
        if *addBlockData == "" {
                addBlockCmd.Usage()
                os.Exit(1)
        }
        cli.addBlock(*addBlockData)
}
if printChainCmd.Parsed() {
        cli.printChain()
}

解析出的子命令該執行的相關函式:

func (cli *CLI) addBlock(data string) {
        cli.bc.AddBlock(data)
        fmt.Println("Success!")
}
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
                }
        }
}

現在的程式碼很像我們之些寫的那些。比較不同的是現在使用的是 BlockchainIterator去遍歷整個區塊鏈中的區塊。 
最後修改 main 函式:

func main() {
        bc := NewBlockchain()
        defer bc.db.Close()
cli := CLI{bc}
        cli.Run()
}

注意,第一次執行時,如果BoltDB中沒有區塊鏈,則無論輸入什麼引數,都會建立一個區塊鏈。 
現在可以檢測一下我們的程式碼是否工作OK了: 
先安裝BoltDB

$ go get github.com/boltdb/bolt/…

執行程式:

$ blockchain_go printchain

No existing blockchain found. Creating a new one… 
Mining the block containing “Genesis Block” 
000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b 
Prev. hash: 
Data: Genesis Block 
Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b 
PoW: true

$ blockchain_go addblock -data “Send 1 BTC to Ivan”

Mining the block containing “Send 1 BTC to Ivan” 
000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 
Success!

$ blockchain_go addblock -data “Pay 0.31337 BTC for a coffee”

Mining the block containing “Pay 0.31337 BTC for a coffee” 
000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148 
Success!

$ blockchain_go printchain

Prev. hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 
Data: Pay 0.31337 BTC for a coffee 
Hash: 000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148 
PoW: true 
Prev. hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b 
Data: Send 1 BTC to Ivan 
Hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 
PoW: true 
Prev. hash: 
Data: Genesis Block 
Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b 
PoW: true 
本章總結 
本章我們實現了區塊的持久化,還有完善了遍歷資訊來支援按序列印所有的區塊。下一章我們將會實現 address,wallet,transaction。敬請期待!

 

相關文章