貨幣是商品和服務的交換媒介。如今,貨幣採用紙幣、硬幣或集中式數字分類賬的形式,通常由政府發行並被普遍接受為一種支付方式。過去,貨幣以各種金屬的形式出現,如金銀,甚至是彩色珠子和鹽。加密貨幣是一種以密碼學和區塊鏈技術為基礎的數字貨幣形式,主要用作轉移價值的一種方式,而無需依賴單一的中心化平台,例如銀行。
在本技術教程中,我們將探討硬幣和代幣之間的區別,您將學習如何開發自己的加密硬幣。
讓我們開始吧!
硬幣和代幣的區別
比特幣是最流行的加密貨幣,其主要目的是作為一種交換媒介。還有許多代幣具有價值,但除了作為一種交換形式的效用之外,還有其他用途:例如,治理投票代幣授予持有人某些治理特權。還有NFT:代表獨特事物所有權的不可替代代幣。那麼,所有這些類型的數字資產之間有什麼區別?
從工程的角度來看,硬幣和代幣之間的區別非常簡單。硬幣是單個區塊鏈的一部分,而代幣以智能合約的形式在現有區塊鏈上運行。
例如,BTC 是比特幣區塊鏈的幣,ETH 是以太坊區塊鏈的幣。 BTC 和ETH 都是硬幣。僅舉幾個例子,USDC、AAVE 和WETH 都是代幣,因為它們本質上是託管在以太坊區塊鏈之上的智能合約。同樣的原則也適用於NFT:它們也是駐留在各種通用區塊鏈上的代幣。
要了解如何創建自己的令牌,請查看 這篇博文。 要了解有關創建NFT 的更多信息,請查看 本指南. 繼續閱讀以了解有關創建自己的加密貨幣的更多信息。
入門
這個項目是用Go 編寫的,但不需要以前有這種語言的經驗。要繼續,請查看位於 Chainlink 智能合約示例存儲庫 在我的加密硬幣文件夾下。
git clone https://github.com/smartcontractkit/smart-contract-examples.git cd smart-contract-examples/my-crypto-coin
下一步是在本地機器上安裝Go,您可以按照官方指南進行操作。由於此過程大約需要10 分鐘,因此您將有時間在完成後煮咖啡。
驗證您的 $GOPATH
在繼續之前正確設置。這是一個強制性步驟。
約定是將源代碼存儲在 $GOPATH/src
和里面的編譯程序二進製文件 $GOPATH/bin
. 導航到 $GOPATH/src
並創建一個名為 my-crypto-coin
.
讓我們從開發開始。
一切從創世區塊開始
硬幣是區塊鏈分佈式賬本中的單位。每個區塊鏈都有其初始狀態,也稱為創世塊。在你新創建的里面 my-crypto-coin
項目,創建一個新文件夾並命名 ledger
. 在- 的里面 ledger
文件夾,新建一個文件,命名 genesis.json
,然後粘貼下面的代碼。我們將分配給Alice 最初的100 萬份我們的加密貨幣。
{ "genesis_time": "2022-04-12T00:00:00.000000000Z", "chain_id": "our-blockchain", "balances": { "alice": 1000000 } }
這是原始狀態。事務改變狀態。如果我們的區塊鏈節點出現故障,我們可以使用創世文件和交易歷史來重新創建整個分類帳並將網絡的其餘部分同步到最新狀態。
賬戶、交易和全局狀態
導航到 ledger
文件夾並創建一個 tx.go
文件。每個帳戶將由帳戶結構表示。每個事務將由事務結構表示,具有以下屬性:“from”、“to”和“value”。我們將添加一個用於創建新帳戶和交易的功能。
package ledger type Account string type Tx struct { From Account `json:"from"` To Account `json:"to"` Value uint `json:"value"` } func NewAccount(value string) Account { return Account(value) } func NewTx(from Account, to Account, value uint) Tx { return Tx{from, to, value} }
交易將存儲在分類帳中,所以讓我們手動添加幾個作為演示。在- 的里面 ledger
目錄,新建一個 ledger.db
文件並在那裡粘貼以下內容。
{"from":"alice","to":"bob","value":10} {"from":"alice","to":"alice","value":3} {"from":"alice","to":"alice","value":500} {"from":"alice","to":"bob","value":100} {"from":"bob","to":"alice","value":5} {"from":"bob","to":"carol","value":10}
創世狀態保持不變並保持在 genesis.json
文件。讓我們添加一種以編程方式加載其狀態的方法。創建一個名為的新文件 genesis.go
,它將在創世狀態存儲具有相應硬幣餘額的帳戶映射。
package ledger import ( "io/ioutil" "encoding/json" ) type Genesis struct { Balances map[Account]uint `json:"balances"` } func loadGenesis(path string) (Genesis, error) { genesisFileContent, err := ioutil.ReadFile(path) if err != nil { return Genesis{}, err } var loadedGenesis Genesis err = json.Unmarshal(genesisFileContent, &loadedGenesis) if err != nil { return Genesis{}, err } return loadedGenesis, nil }
核心業務邏輯將存儲在Store 結構中。創建一個名為的新文件 state.go
. 狀態結構將包含所有賬戶餘額的詳細信息,誰將硬幣轉移給誰,以及轉移了多少硬幣。它必須知道如何從genesis 文件中讀取初始狀態。之後,創世狀態餘額按順序更新 重放所有交易 ledger.db
文件。最後,在這裡我們需要編寫一個將新交易添加到分類帳的邏輯。
package ledger import ( "fmt" "os" "path/filepath" "bufio" "encoding/json" ) type State struct { Balances map[Account]uint txMempool []Tx dbFile *os.File } func SyncState() (*State, error) { cwd, err := os.Getwd() if err != nil { return nil, err } gen, err := loadGenesis(filepath.Join(cwd, "ledger", "genesis.json")) if err != nil { return nil, err } balances := make(map[Account]uint) for account, balance := range gen.Balances { balances[account] = balance } file, err := os.OpenFile(filepath.Join(cwd, "ledger", "ledger.db"), os.O_APPEND|os.O_RDWR, 0600) if err != nil { return nil, err } scanner := bufio.NewScanner(file) state := &State{balances, make([]Tx, 0), file} for scanner.Scan() { if err := scanner.Err(); err != nil { return nil, err } var transaction Tx json.Unmarshal(scanner.Bytes(), &transaction) if err := state.writeTransaction(transaction); err != nil { return nil, err } } return state, nil } func (s *State) writeTransaction(tx Tx) error { if s.Balances[tx.From] < tx.Value { return fmt.Errorf("insufficient balance") } s.Balances[tx.From] -= tx.Value s.Balances[tx.To] += tx.Value return nil } func (s *State) Close() { s.dbFile.Close() } func (s *State) WriteToLedger(tx Tx) error { if err := s.writeTransaction(tx); err != nil { return err } s.txMempool = append(s.txMempool, tx) mempool := make([]Tx, len(s.txMempool)) copy(mempool, s.txMempool) for i := 0; i < len(mempool); i++ { txJson, err := json.Marshal(mempool[i]) if err != nil { return err } if _, err = s.dbFile.Write(append(txJson, 'n')); err != nil { return err } s.txMempool = s.txMempool[1:] } return nil }
開發命令行界面(CLI)
使用我們的新加密貨幣最簡單的方法是開發一個命令行界面(CLI)。在Go 中開發基於CLI 的程序的最簡單方法是使用 下一個第三方庫. 要使用這個庫,我們需要為我們的項目初始化Go 的內置依賴管理器,稱為Go 模塊。 Go 模塊命令將自動獲取您在Go 文件中引用的任何庫。
echo $GOPATH cd $GOPATH/src/my-crypto-coin go mod init
現在讓我們創建一個新文件夾並命名它 cli
. 但是等等,我們還沒有正式命名我們的加密貨幣!我們暫時稱它為My Crypto Coin。導航到 cli
文件夾並創建一個名為 mcc
,是My Crypto Coin 的縮寫。導航到 mcc
文件夾。
創建一個名為的新文件 main.go
. 這將是我們程序的主要入口。
package main import ( "github.com/spf13/cobra" "os" "fmt" ) func main() { var mccCmd = &cobra.Command{ Use: "mcc", Short: "My Crypto Coin CLI", Run: func(cmd *cobra.Command, args []string) { }, } mccCmd.AddCommand(versionCmd) mccCmd.AddCommand(balancesCmd()) mccCmd.AddCommand(txCmd()) err := mccCmd.Execute() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
接下來,創建一個 version.go
文件並粘貼此內容。
package main import ( "fmt" "github.com/spf13/cobra" ) const Major = "0" const Minor = "1" const Fix = "0" const Verbal = "Genesis" var versionCmd = &cobra.Command{ Use: "version", Short: "Describes version.", Run: func(cmd *cobra.Command, args []string) { fmt.Println(fmt.Sprintf("Version: %s.%s.%s-beta %s", Major, Minor, Fix, Verbal)) }, }
之後,讓我們創建一種機制來從分類帳中讀取所有帳戶餘額。創建一個新的 balances.go
文件。
package main import ( "github.com/spf13/cobra" "my-crypto-coin/ledger" "fmt" "os" ) func balancesCmd() *cobra.Command { var balancesCmd = &cobra.Command{ Use: "balances", Short: "Interact with balances (list...).", Run: func(cmd *cobra.Command, args []string) { }, } balancesCmd.AddCommand(balancesListCmd) return balancesCmd } var balancesListCmd = &cobra.Command{ Use: "list", Short: "Lists all balances.", Run: func(cmd *cobra.Command, args []string) { state, err := ledger.SyncState() 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)) } }, }
最後,讓我們添加一個用於將交易寫入分類帳的命令。創建新的 tx.go
文件。
package main import ( "github.com/spf13/cobra" "my-crypto-coin/ledger" "fmt" "os" ) func txCmd() *cobra.Command { var txsCmd = &cobra.Command{ Use: "tx", Short: "Interact with transactions (new...).", Run: func(cmd *cobra.Command, args []string) { }, } txsCmd.AddCommand(newTxCmd()) return txsCmd } func newTxCmd() *cobra.Command { var cmd = &cobra.Command{ Use: "new", Short: "Adds new TX to the ledger.", Run: func(cmd *cobra.Command, args []string) { from, _ := cmd.Flags().GetString("from") to, _ := cmd.Flags().GetString("to") value, _ := cmd.Flags().GetUint("value") tx := ledger.NewTx(ledger.NewAccount(from), ledger.NewAccount(to), value) state, err := ledger.SyncState() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer state.Close() err = state.WriteToLedger(tx) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Println("TX successfully added to the ledger.") }, } cmd.Flags().String("from", "", "From what account to send coins") cmd.MarkFlagRequired("from") cmd.Flags().String("to", "", "To what account to send coins") cmd.MarkFlagRequired("to") cmd.Flags().Uint("value", 0, "How many coins to send") cmd.MarkFlagRequired("value") return cmd }
使用以下命令編譯程序:
go install $GOPATH/src/my-crypto-coin/cli/mcc/…
Go 將檢測丟失的庫並在編譯程序之前自動獲取它們。取決於你的 $GOPATH
,生成的程序將保存在 $GOPATH/bin
文件夾。
要驗證安裝是否成功,請運行以下命令:
which mcc
您應該在終端中看到與此類似的路徑: /Users/yourname/go/bin/mcc
. 讓我們看看所有可用的命令。跑:
mcc --help
要查看CLI 的當前版本,請運行:
mcc version
要查看當前用戶餘額,請運行以下命令:
mcc balances list
輸出應該是:
Accounts balances: __________________ alice: 999895 bob: 95 carol: 10
現在讓我們使用CLI 進行第一個事務。輸入以下命令:
mcc tx new --from alice --to carol --value 10
如果你打開 ledger/ledger.db
文件,您應該能夠看到額外的一行:
{"from":"alice","to":"carol","value":10}
讓我們再次使用 mcc balances list
命令。輸出應該是:
Accounts balances: __________________ alice: 999885 bob: 95 carol: 20
下一步是什麼?
目前,我們的用戶在賬本中用他們的名字表示。但是如果有兩個Bob 會發生什麼呢?我們需要添加一種使用通用散列算法唯一表示帳戶的方法。
下一個問題是任何人都可以使用我們的CLI 轉移其他人的代幣。我們需要一種方法來允許用戶使用公鑰密碼術只轉移他們擁有的硬幣。
如果我們的機器出現故障會發生什麼?沒有辦法重新創建我們的網絡,因為我們是網絡中唯一的節點。我們需要激勵人們加入我們的網絡並成為節點。一旦我們的網絡增長,我們將需要一種通用的方法來確定哪些交易是有效的,哪些不是,並驗證網絡的當前狀態。我們需要一個共識算法和機制。
概括
在本文中,您學習瞭如何使用Go 開發基本的加密貨幣,並且我們已經介紹了硬幣和代幣之間的主要區別。要了解更多信息,請前往 Chainlink 智能合約示例存儲庫 並開始試驗這個和其他示例項目。
通過訪問了解有關Chainlink 的更多信息 鏈環 或閱讀文檔 docs.chain.link. 要討論集成,請聯繫專家。