Как создать свой блокчейн с нуля при помощи Go? Часть 2

Делитесь и голосуйте:

Содержание статьи:

  • 3. Создание интерфейса командной строки (CLI) 
  • Интерфейс командной строки готов!

Цель этого гайда - разобрать технологические составляющие блокчейна на практике. Для этого рассмотрим кейс разработки собственного блокчейна с нуля.

В первой части мы пошагово разобрали процесс генерации первых токенов и изменение состояние базы данных. В этой части взглянем на процесс создания интерфейса командной строки

Андрею нужен удобный способ добавлять новые транзакции в свою базу данных и выводить список последних балансов своих клиентов. Для этого он создает интерфейс командной строки.

Самый простой способ разработать CLI программу на GO - это использовать стороннюю библиотеку github.com/spf13/cobra.

Андрей подключает встроенного в Go менеджера go modules:

? cd $GOPATH/src/github.com/web3coach/the-blockchain-way-of-programming-newsletter-edition
? go mod init github.com/web3coach/the-blockchain-way-of-programming-newsletter-edition

Команда Go modules будет автоматически подтягивать любую библиотеку, которую вы выберите в файлах Go.

Андрей создает новый каталог под названием cmd с подкаталогом tbb:

?mkdir -p ./cmd/tbb

Внутри каталога он создает файл main.go, служащий отправной точкой CLI программы.

package mainimport (    "github.com/spf13/cobra"    "os"    "fmt")func main() {    var tbbCmd = &cobra.Command{        Use:   "tbb",        Short: "The Blockchain Bar CLI",        Run: func(cmd *cobra.Command, args []string) {        },    }    err := tbbCmd.Execute()    if err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }}

Программы GO компилируются при помощи команды install :

? go install ./cmd/tbb/...go: finding github.com/spf13/cobra v1.0.0go: downloading github.com/spf13/cobra v1.0.0go: extracting github.com/spf13/cobra v1.0.0

Go будет находить недостающие библиотеки и автоматически подтягивать их перед тем как компилировать программу. В зависимости от $GOPATH результат будет сохраняться в папку $GOPATH/bin.

?echo $GOPATH/home/web3coach/go?which tbb/home/web3coach/go/bin/tbb

Вы можете запустить tbb из своего терминала хоть сейчас, но ничего не произойдет, так как функция Run внутри файла main.go пуста. 

Для начала Андрею нужно управлять версиями (versioning support) для tbb. Рядом с файлом main.go он создает команду version.go:

 package mainimport (    "fmt"    "github.com/spf13/cobra")const Major = "0"const Minor = "1"const Fix = "0"const Verbal = "TX Add && Balances List"var versionCmd = &cobra.Command{    Use:   "version",    Short: "Describes version.",    Run: func(cmd *cobra.Command, args []string) {        fmt.Printf("Version: %s.%s.%s-beta %s", Major, Minor, Fix, Verbal)    },}

Компилирует ее и запускает:

? go install ./cmd/tbb/...? tbb versionVersion: 0.1.0-beta TX Add && Balances List

Готово.

Идентично с файлом version.go он создает файл balances.go

func balancesCmd() *cobra.Command {    var balancesCmd = &cobra.Command{        Use:   "balances",        Short: "Interact with balances (list...).",        PreRunE: func(cmd *cobra.Command, args []string) error {            return incorrectUsageErr()        },        Run: func(cmd *cobra.Command, args []string) {        },    }    balancesCmd.AddCommand(balancesListCmd)   return balancesCmd}

Команда balances будет ответственна за загрузку последнего State базы данных и отображение информации:

var balancesListCmd = &cobra.Command{    Use:   "list",    Short: "Lists all balances.",    Run: func(cmd *cobra.Command, args []string) {        state, err := database.NewStateFromDisk()        if err != nil {            fmt.Fprintln(os.Stderr, err)            os.Exit(1)        }        defer state.Close()        fmt.Println("Accounts balances:")        fmt.Println("__________________")        fmt.Println("")        for account, balance := range state.Balances {            fmt.Println(fmt.Sprintf("%s: %d", account, balance))        }    },}

Теперь команда должна выдавать те же балансы, которые указаны в файле Genesis, так как tx.db все еще пуста.

? go install ./cmd/tbb/...? tbb balances listAccounts balances:__________________andrej: 1000000

Все работает как надо. Теперь ему нужна команда для записи деятельности бара. Андрей создает команду ./cmd/tbb/tx.go

func txCmd() *cobra.Command {    var txsCmd = &cobra.Command{        Use:   "tx",        Short: "Interact with txs (add...).",        PreRunE: func(cmd *cobra.Command, args []string) error {            return incorrectUsageErr()        },        Run: func(cmd *cobra.Command, args []string) {        },    }    txsCmd.AddCommand(txAddCmd())    return txsCmd}

Команда tbb tx add использует функцию State.Add(tx) для того, чтобы вносить события в баре в систему:

func txAddCmd() *cobra.Command {    var cmd = &cobra.Command{        Use:   "add",        Short: "Adds new TX to database.",        Run: func(cmd *cobra.Command, args []string) {            from, _ := cmd.Flags().GetString(flagFrom)            to, _ := cmd.Flags().GetString(flagTo)            value, _ := cmd.Flags().GetUint(flagValue)            fromAcc := database.NewAccount(from)            toAcc := database.NewAccount(to)            tx := database.NewTx(fromAcc, toAcc, value, "")            state, err := database.NewStateFromDisk()            if err != nil {                fmt.Fprintln(os.Stderr, err)                os.Exit(1)            }              // defer means, at the end of this function execution,            // execute the following statement (close DB file with all TXs)            defer state.Close()            // Add the TX to an in-memory array (pool)            err = state.Add(tx)            if err != nil {                fmt.Fprintln(os.Stderr, err)                os.Exit(1)            }            // Flush the mempool TXs to disk            err = state.Persist()            if err != nil {                fmt.Fprintln(os.Stderr, err)                os.Exit(1)            }            fmt.Println("TX successfully added to the ledger.")        },    }

Команда tbb tx add содержит три необходимых элемента: --from, --to , --value.

cmd.Flags().String(flagFrom, "", "From what account to send tokens")cmd.MarkFlagRequired(flagFrom)cmd.Flags().String(flagTo, "", "To what account to send tokens")cmd.MarkFlagRequired(flagTo)cmd.Flags().Uint(flagValue, 0, "How many tokens to send")cmd.MarkFlagRequired(flagValue)return cmd

Андрей переносит все записи со своей бумажной базы данных в электронную:

? tbb tx add --from=andrej --to=andrej --value=3
?tbb tx add --from=andrej --to=andrej --value=700
?tbb tx add --from=babayaga --to=andrej --value=2000
?tbb tx add --from=andrej --to=andrej --value=100 --data=reward
?tbb tx add --from=babayaga --to=andrej --value=1

Теперь он рассчитывает конечные балансы клиентов:

? tbb balances list

Accounts balances:

__________________

andrej: 998801

babayaga: 1999

Данные бара успешно восстановлены! 

В следующей части мы рассмотрим принцип работы комиссий в блокчейне, зачем вообще нужен блокчейн и как сделать базу данных неизменной.

Государство и общество

Ждем новостей

Нет новых страниц

Следующая новость