Цель этого гайда - разобрать технологические составляющие блокчейна на практике. Для этого рассмотрим кейс разработки собственного блокчейна с нуля.
В первой части мы пошагово разобрали процесс генерации первых токенов и изменение состояние базы данных. В этой части взглянем на процесс создания интерфейса командной строки
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 main
import (
"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.0
go: downloading github.com/spf13/cobra v1.0.0
go: 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 main
import (
"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 version
Version: 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 list
Accounts 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
Данные бара успешно восстановлены!
В следующей части мы рассмотрим принцип работы комиссий в блокчейне, зачем вообще нужен блокчейн и как сделать базу данных неизменной.
Подписывайтесь на ForkNews в Telegram, чтобы быть в курсе новостей из мира криптовалют
По материалам www.freecodecamp.org
Читайте также
Как собрать майнинг риг для Bitcoin и Ether в 2020 году?
Майнинг на небольших домашних фермах вновь становится популярным. Что нужно для того, чтобы собрать собственный риг дома?
Что такое сатоши (Satoshi) биткоина?
Подобно многим фиатным валютам, биткоин имеет наименьшую денежную единицу - сатоши.