您好,登錄后才能下訂單哦!
從0到1簡易區塊鏈開發手冊V0.2-創建錢包
https://blog.51cto.com/clovemfong/2161923
從0到1簡易區塊鏈開發手冊V0.3-數據持久化與創世區塊
https://blog.51cto.com/clovemfong/2162169
從0到1簡易區塊鏈開發手冊V0.4-實現轉賬交易的思路分析
https://blog.51cto.com/clovemfong/2163057
從0到1簡易區塊鏈開發手冊V0.5-實現余額查詢
https://blog.51cto.com/clovemfong/2163109
從0到1簡易區塊鏈開發手冊V0.6-實現打印區塊
https://blog.51cto.com/clovemfong/2163211
這是我這段時間學習區塊鏈開發以來打造的第一個區塊鏈平臺,之所以叫做簡易區塊鏈,是因為它確實比較簡易,僅僅是實現了底層的一些功能,不足以作為一個真正的公鏈使用,但通過學習,我們能夠通過代碼更加理解比特幣白皮書中描述的各種比特幣原理,區塊鏈世界,無論是研究理論的,還是實戰開發的,甚至炒幣玩資本的,都離不開比特幣的影子,區塊鏈技術畢竟是從比特幣中剝離抽象而來,所以,作為一個技術人員,無論是研究以太坊,超級賬本,甚至是各種公鏈,包括某些山寨公鏈,都需要先去理解比特幣原理,而對于開發者而言,理解原理最好的方式就是將其通過代碼實現,當然,我們這里實現的一些原理只是應用實戰范圍之內可以實現的,比如橢圓加密算法,我們要實現的只是使用橢圓加密去實現某些加密功能,而非用代碼去實現一個完整的橢圓加密代碼庫,這個不再本文的討論范圍內,所以本文面向的群體是:
本文中,我們先通過命令行的方式演示區塊鏈的工作流程以及相關原理,涉及到比較重要的內容,比如Sha256哈希,橢圓加密,Base58編碼等內容,我會根據時間以及后期的工作情況進行適當調整,這注定是一個短期內沒有結尾的故事。
為表尊敬,寫在前面,建議先閱讀該文檔
本文的學習資料來自這位liuxhengxu前輩翻譯的資料
能將資料翻譯得如此完美,相比其技術功能也是相當深厚的,感謝分享
建議大家可以先看該資料后再來看我的這系列文章,否則可能會有一些難度,由于該資料是通過循序漸進的方式進行版本迭代,慢慢引導開發者不斷對原有的代碼進行優化,拓展,非常認真并細心,希望大家時間充裕的時候以及對某些本文并未寫清楚的地方,強烈建議閱讀該資料。
本文在此基礎上進行了一些修改(談不上改進),我摒棄一些過于基礎的以及后期需要大量重構的代碼,直接通過該項目的執行流程進行代碼分析,這樣可以稍微節省一些大家的時間,把有限的精力放在對業務有更大提升的技術研究上。
Usage:
createwallet
-- 創建錢包
getaddresslists
-- 獲取所有的錢包地址
createblockchain -address address
-- 創建創世區塊
send -from SourceAddress -to DestAddress -amount Amount
-- 轉賬交易
printchain
-- 打印區塊
getbalance -address address
-- 查詢余額
本文圍繞著幾個功能進行講解
創建錢包
通過橢圓加密算法創建錢包地址
獲取錢包地址
獲取區塊鏈中所有的錢包地址
創建創世區塊
實現創世區塊的創建,并生成區塊鏈
實現轉賬交易
通過轉賬交易,生成區塊,并存入區塊鏈
打印區塊
打印出所有的區塊信息,實現轉賬交易的溯源
查詢余額
查詢出對應錢包的余額狀態
隨著代碼的不斷完善,我們將會對以上進行改進,并提供更多的功能點進行分析探討,我們先通過下圖簡單演示一下如上功能
關于效果圖,大家先大致看下即可,不需要刻意研究,在后期的課程中都會涉及。
定義一個空結構體
type CLI struct {
}
重要!初學者必看
這里提到的結構體方法并不是真正實現功能的方法,而是命令行對象的方法,這些方法中會調用實際的功能對象方法進行功能實現,在本章節中,創建結構體方法即可,功能代碼可以為空,如:
例子:
func (cli *CLI) CreateWallet() {
}
func (cli *CLI) GetAddressLists() {
}
.....
其他的可以在后期逐步實現,為了讓有基礎的同學對項目整體提前有些印象,所以,代碼內容我直接復制粘貼進來,不做刪減,在后期的內容中,會逐步涉及到每個調用的對象方法或者函數的作用。
func (cli *CLI) CreateWallet() {
_, wallets := GetWallets() //獲取錢包集合對象
wallets.CreateNewWallets() //創建錢包集合
}
func (cli *CLI) GetAddressLists() {
fmt.Println("錢包地址列表為:")
//獲取錢包的集合,遍歷,依次輸出
_, wallets := GetWallets() //獲取錢包集合對象
for address, _ := range wallets.WalletMap {
fmt.Printf("\t%s\n", address)
}
}
func (cli *CLI) CreateBlockChain(address string) {
CreateBlockChainWithGenesisBlock(address)
bc :=GetBlockChainObject()
if bc == nil{
fmt.Println("沒有數據庫")
os.Exit(1)
}
defer bc.DB.Close()
utxoSet:=&UTXOSet{bc}
utxoSet.ResetUTXOSet()
}
func (cli *CLI) Send(from, to, amount []string) {
bc := GetBlockChainObject()
if bc == nil {
fmt.Println("沒有BlockChain,無法轉賬。。")
os.Exit(1)
}
defer bc.DB.Close()
bc.MineNewBlock(from, to, amount)
//添加更新
utsoSet :=&UTXOSet{bc}
utsoSet.Update()
}
func (cli *CLI) GetBalance(address string) {
bc := GetBlockChainObject()
if bc == nil {
fmt.Println("沒有BlockChain,無法查詢。。")
os.Exit(1)
}
defer bc.DB.Close()
//total := bc.GetBalance(address,[]*Transaction{})
utxoSet :=&UTXOSet{bc}
total:=utxoSet.GetBalance(address)
fmt.Printf("%s,余額是:%d\n", address, total)
}
func (cli *CLI) PrintChains() {
//cli.BlockChain.PrintChains()
bc := GetBlockChainObject() //bc{Tip,DB}
if bc == nil {
fmt.Println("沒有BlockChain,無法打印任何數據。。")
os.Exit(1)
}
defer bc.DB.Close()
bc.PrintChains()
}
func isValidArgs() {
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
}
判斷終端命令是否有參數輸入,如果沒有參數,則提示程序使用說明,并退出程序
func printUsage() {
fmt.Println("Usage:")
fmt.Println("\tcreatewallet\n\t\t\t-- 創建錢包")
fmt.Println("\tgetaddresslists\n\t\t\t-- 獲取所有的錢包地址")
fmt.Println("\tcreateblockchain -address address\n\t\t\t-- 創建創世區塊")
fmt.Println("\tsend -from SourceAddress -to DestAddress -amount Amount\n\t\t\t-- 轉賬交易")
fmt.Println("\tprintchain\n\t\t\t-- 打印區塊")
fmt.Println("\tgetbalance -address address\n\t\t\t-- 查詢余額")
}
func JSONToArray(jsonString string) []string {
var arr [] string
err := json.Unmarshal([]byte(jsonString), &arr)
if err != nil {
log.Panic(err)
}
return arr
}
通過該函數將JSON字符串格式轉成字符串數組,用于在多筆轉賬交易中實現同時多個賬戶進行兩兩轉賬的功能。
func IsValidAddress(address []byte) bool {
//step1:Base58解碼
//version+pubkeyHash+checksum
full_payload := Base58Decode(address)
//step2:獲取地址中攜帶的checkSUm
checkSumBytes := full_payload[len(full_payload)-addressCheckSumLen:]
versioned_payload := full_payload[:len(full_payload)-addressCheckSumLen]
//step3:versioned_payload,生成一次校驗碼
checkSumBytes2 := CheckSum(versioned_payload)
//step4:比較checkSumBytes,checkSumBytes2
return bytes.Compare(checkSumBytes, checkSumBytes2) == 0
}
以下三個功能實現之前需要先調用該函數進行地址校驗
Usage:
createwallet
-- 創建錢包
getaddresslists
-- 獲取所有的錢包地址
createblockchain -address address
-- 創建創世區塊
send -from SourceAddress -to DestAddress -amount Amount
-- 轉賬交易
printchain
-- 打印區塊
getbalance -address address
-- 查詢余額
我們將如上功能展示的實現功能寫在Run方法中,實現命令行功能的關鍵是了解os.Args與flag
關于這兩個功能,此處不再贅述,否則篇幅會無限臃腫。
代碼塊均在方法體Run中,下文將分步驟對代碼實現進行體現
func (cli *CLI) Run() {
}
isValidArgs()
createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError)
getAddresslistsCmd := flag.NewFlagSet("getaddresslists", flag.ExitOnError)
CreateBlockChainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)
sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError)
testMethodCmd := flag.NewFlagSet("test", flag.ExitOnError)
如上,通過flag.NewFlagSet方法創建命令對象,如createwallet,getaddresslists,createblockchain等命令對象
固定用法,掌握即可。
flagCreateBlockChainData := CreateBlockChainCmd.String("address", "GenesisBlock", "創世區塊的信息")
flagSendFromData := sendCmd.String("from", "", "轉賬源地址")
flagSendToData := sendCmd.String("to", "", "轉賬目標地址")
flagSendAmountData := sendCmd.String("amount", "", "轉賬金額")
flagGetBalanceData := getBalanceCmd.String("address", "", "要查詢余額的賬戶")
通過命令對象的String方法為命令后的參數對象
其中createwallet,getaddresslists,printchain命令沒有參數對象。
switch os.Args[1] {
case "createwallet":
err := createWalletCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "getaddresslists":
err := getAddresslistsCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "createblockchain":
err := CreateBlockChainCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "send":
err := sendCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "getbalance":
err := getBalanceCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "printchain":
err := printChainCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "test":
err := testMethodCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
default:
printUsage()
os.Exit(1)
}
匹配對應的命令,用命令對象的Parse方法對os.Args[2:]進行解析。
//4.1 創建錢包--->交易地址
if createWalletCmd.Parsed() {
cli.CreateWallet()
}
//4.2 獲取錢包地址
if getAddresslistsCmd.Parsed() {
cli.GetAddressLists()
}
//4.3 創建創世區塊
if CreateBlockChainCmd.Parsed() {
if !IsValidAddress([]byte(*flagCreateBlockChainData)) {
fmt.Println("地址無效,無法創建創世前區塊")
printUsage()
os.Exit(1)
}
cli.CreateBlockChain(*flagCreateBlockChainData)
}
//4.4 轉賬交易
if sendCmd.Parsed() {
if *flagSendFromData == "" || *flagSendToData == "" || *flagSendAmountData == "" {
fmt.Println("轉賬信息有誤")
printUsage()
os.Exit(1)
}
//添加區塊
from := JSONToArray(*flagSendFromData) //[]string
to := JSONToArray(*flagSendToData) //[]string
amount := JSONToArray(*flagSendAmountData) //[]string
for i := 0; i < len(from); i++ {
if !IsValidAddress([]byte(from[i])) || !IsValidAddress([]byte(to[i])) {
fmt.Println("地址無效,無法轉賬")
printUsage()
os.Exit(1)
}
}
cli.Send(from, to, amount)
}
//4.5 查詢余額
if getBalanceCmd.Parsed() {
if !IsValidAddress([]byte(*flagGetBalanceData)) {
fmt.Println("查詢地址有誤")
printUsage()
os.Exit(1)
}
cli.GetBalance(*flagGetBalanceData)
}
//4.6 打印區塊信息
if printChainCmd.Parsed() {
cli.PrintChains()
}
在main.go中中添加測試代碼
package main
func main() {
cli:=BLC.CLI{}
cli.Run()
}
編譯運行
$ go build -o mybtc main.go
測試思路
- 查看命令行列表是否可以正常顯示
- 輸入非法字符查看是否有錯誤提示
業務功能此處暫未實現,測試時忽略。
從0到1簡易區塊鏈開發手冊V0.2-創建錢包
https://blog.51cto.com/clovemfong/2161923
從0到1簡易區塊鏈開發手冊V0.3-數據持久化與創世區塊
https://blog.51cto.com/clovemfong/2162169
從0到1簡易區塊鏈開發手冊V0.4-實現轉賬交易的思路分析
https://blog.51cto.com/clovemfong/2163057
從0到1簡易區塊鏈開發手冊V0.5-實現余額查詢
https://blog.51cto.com/clovemfong/2163109
從0到1簡易區塊鏈開發手冊V0.6-實現打印區塊
https://blog.51cto.com/clovemfong/2163211
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。