使用golang从零开始搭建基于UTXO模型的区块链(五、构建命令行客户端调试)

前言

我们继续上一章没进行完的内容,构建一个客户端

命令行

我们希望用命令行的方式来管理我们的区块链系统,我们使用flag库来实现。
创建cli.go,代码如下

package cli

import (
	"bytes"
	"flag"
	"fmt"
	"lighteningchain/blockchain"
	"lighteningchain/utils"
	"os"
	"runtime"
	"strconv"
)

type CommandLine struct{
    
    }

func (cli *CommandLine) printUsage() {
    
    
	fmt.Println("Welcome to Arno's LighteningChain System!")
	fmt.Println("===================================================")
	fmt.Println("Usage:")
	fmt.Println("---------------------------------------------------")
	fmt.Println("To get started, create a new blockchain and declare the owner's address. " +
		"You can then make transactions and mine blocks to add to the blockchain.")
	fmt.Println("---------------------------------------------------")
	fmt.Println("Commands:")
	fmt.Println("---------------------------------------------------")
	fmt.Println("createblockchain -address ADDRESS  -> " +
		"Create a new blockchain with the specified owner's address")
	fmt.Println("balance -address ADDRESS           -> " +
		"Check the balance of a specific address in the blockchain")
	fmt.Println("blockchaininfo                      -> " +
		"View information about all blocks in the blockchain")
	fmt.Println("send -from FROMADDRESS -to TOADDRESS -amount AMOUNT  -> " +
		"Create a new transaction and add it to the candidate block for mining")
	fmt.Println("mine                                -> " +
		"Mine a block and add it to the blockchain")
	fmt.Println("===================================================")
}
func (cli *CommandLine) createBlockChain(address string) {
    
    
	newChain := blockchain.InitBlockChain([]byte(address))
	newChain.Database.Close()
	fmt.Println("Finished creating blockchain, and the owner is: ", address)
}
func (cli *CommandLine) balance(address string) {
    
    
	chain := blockchain.LoadBlockChain()
	defer chain.Database.Close()

	balance, _ := chain.FindUTXOs([]byte(address))
	fmt.Printf("Address:%s, Balance:%d \n", address, balance)
}

// getblockchaininfo命令需要使用我们之前设计的迭代器遍历区块链。
func (cli *CommandLine) getBlockChainInfo() {
    
    
	chain := blockchain.LoadBlockChain()
	defer chain.Database.Close()
	iterator := chain.Iterator()
	ogprevhash := chain.BackOgPrevHash()
	for {
    
    
		block := iterator.Next()
		fmt.Println("--------------------------------------------------------------------------------------------------------------")
		fmt.Printf("Timestamp:%d\n", block.Timestamp)
		fmt.Printf("Previous hash:%x\n", block.PrevHash)
		fmt.Printf("Transactions:%v\n", block.Transactions)
		fmt.Printf("hash:%x\n", block.Hash)
		fmt.Printf("Pow: %s\n", strconv.FormatBool(block.ValidatePoW()))
		fmt.Println("--------------------------------------------------------------------------------------------------------------")
		fmt.Println()
		if bytes.Equal(block.PrevHash, ogprevhash) {
    
    
			break
		}
	}
}

// send命令将会调用CreateTransaction函数,并将创建的交易信息保存到交易信息池中。
func (cli *CommandLine) send(from, to string, amount int) {
    
    
	chain := blockchain.LoadBlockChain()
	defer chain.Database.Close()
	tx, ok := chain.CreateTransaction([]byte(from), []byte(to), amount)
	if !ok {
    
    
		fmt.Println("Failed to create transaction")
		return
	}
	tp := blockchain.CreateTransactionPool()
	tp.AddTransaction(tx)
	tp.SaveFile()
	fmt.Println("Success!")
}

// mine命令调用RunMine即可
func (cli *CommandLine) mine() {
    
    
	chain := blockchain.LoadBlockChain()
	defer chain.Database.Close()
	chain.RunMine()
	fmt.Println("Finish Mining")
}

// 使用flag库将各命令注册即可
func (cli *CommandLine) validateArgs() {
    
    
	if len(os.Args) < 2 {
    
    
		cli.printUsage()
		runtime.Goexit()
	}
}

func (cli *CommandLine) Run() {
    
    
	cli.validateArgs()

	createBlockChainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)
	balanceCmd := flag.NewFlagSet("balance", flag.ExitOnError)
	getBlockChainInfoCmd := flag.NewFlagSet("blockchaininfo", flag.ExitOnError)
	sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
	mineCmd := flag.NewFlagSet("mine", flag.ExitOnError)

	createBlockChainOwner := createBlockChainCmd.String("address", "", "The address refer to the owner of blockchain")
	balanceAddress := balanceCmd.String("address", "", "Who need to get balance amount")
	sendFromAddress := sendCmd.String("from", "", "Source address")
	sendToAddress := sendCmd.String("to", "", "Destination address")
	sendAmount := sendCmd.Int("amount", 0, "Amount to send")

	switch os.Args[1] {
    
    
	case "createblockchain":
		err := createBlockChainCmd.Parse(os.Args[2:])
		utils.Handle(err)

	case "balance":
		err := balanceCmd.Parse(os.Args[2:])
		utils.Handle(err)

	case "blockchaininfo":
		err := getBlockChainInfoCmd.Parse(os.Args[2:])
		utils.Handle(err)

	case "send":
		err := sendCmd.Parse(os.Args[2:])
		utils.Handle(err)

	case "mine":
		err := mineCmd.Parse(os.Args[2:])
		utils.Handle(err)

	default:
		cli.printUsage()
		runtime.Goexit()
	}

	if createBlockChainCmd.Parsed() {
    
    
		if *createBlockChainOwner == "" {
    
    
			createBlockChainCmd.Usage()
			runtime.Goexit()
		}
		cli.createBlockChain(*createBlockChainOwner)
	}

	if balanceCmd.Parsed() {
    
    
		if *balanceAddress == "" {
    
    
			balanceCmd.Usage()
			runtime.Goexit()
		}
		cli.balance(*balanceAddress)
	}

	if sendCmd.Parsed() {
    
    
		if *sendFromAddress == "" || *sendToAddress == "" || *sendAmount <= 0 {
    
    
			sendCmd.Usage()
			runtime.Goexit()
		}
		cli.send(*sendFromAddress, *sendToAddress, *sendAmount)
	}

	if getBlockChainInfoCmd.Parsed() {
    
    
		cli.getBlockChainInfo()
	}

	if mineCmd.Parsed() {
    
    
		cli.mine()
	}
}

这样我们就分别实现了区块链的各种功能。

  1. createblockchain -address ADDRESS: 创建一个新的区块链,并指定拥有者的地址。
  2. balance -address ADDRESS: 查询指定地址的余额。
  3. blockchaininfo: 查看区块链中所有区块的信息。
  4. send -from FROMADDRESS -to TOADDRESS -amount AMOUNT:
    创建一笔交易,并将其加入候选区块以进行挖矿。
  5. mine: 挖掘一个区块并将其添加到区块链中。

调试

main函数我们只需要放一个入口就可以

package main

import (
	"lighteningchain/cli"
	"os"
)

func main() {
    
    
	defer os.Exit(0)
	cmd := cli.CommandLine{
    
    }
	cmd.Run()
}

如上,我们现在就可以开始编译我们的go程序了。
首先输入go build main.go创建可执行文件
在这里插入图片描述
接下来我们便可以运行了,使用命令行和goland中自带的控制台都可以
请添加图片描述
另外我们在tmp文件夹下创建一个blocks路径,暂存我们的区块
在这里插入图片描述
没输入命令的话,程序会自动退出,我们首先创建一个区块链,地址就写自己
输入:.\main.exe createblockchain -address Arno
输出:Genesis Created Finished creating blockchain, and the owner is: Arno
后面的大家可以自己测一测,我就不一一截图了。
当然上一章提到的双花bug还是存在的,这就需要我们在RunMine函数中增加对交易信息池中交易信息的验证,这将会在以后的章节中实现。

总结

这两章我们讲解并实现了区块链的存储与读取,同时更加深入地了解了交易信息池与挖矿过程,最终建立了一个命令行程序来管理我们的区块链系统功能。在下一章我们将会进入虚拟钱包wallet的讲解,这会涉及到区块链的核心内容非对称密钥等相关知识。

猜你喜欢

转载自blog.csdn.net/qq_44232564/article/details/129364575