日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

r语言error in match.fun(fun) :_Go语言200行写区块链源代码分析

發布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 r语言error in match.fun(fun) :_Go语言200行写区块链源代码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Github上有一個Repo,是一個使用Go語言(golang),不到200行代碼寫的區塊鏈源代碼,準確的說是174行。原作者起了個名字是 Code your own blockchain in less than 200 lines of Go! 而且作者也為此寫了一篇文章。https://medium.com/@mycoralhealth/code-your-own-blockchain-in-less-than-200-lines-of-go-e296282bcffc

這篇文章是一個大概的思路和代碼的實現,當然還有很多代碼的邏輯沒有涉及,所以我就針對這不到200行的代碼進行一個分析,包含原文章里沒有涉及到的知識點,對Go語言,區塊鏈都會有一個更深的認識。

所有的源代碼都在這里:https://github.com/nosequeldeebee/blockchain-tutorial/blob/master/main.go

import?(
????"crypto/sha256"
????"encoding/hex"
????"encoding/json"
????"io"
????"log"
????"net/http"
????"os"
????"strconv"
????"sync"
????"time"

????"github.com/davecgh/go-spew/spew"
????"github.com/gorilla/mux"
????"github.com/joho/godotenv"
)

在源代碼的開頭,是作者引入的一些包,有標準的,也有第三方的。像sha256,hex這些標準包是為了sha-256編碼用的,其他還有啟動http服務,打印日志的log,并發控制的sync,時間戳的time。

第三方包有三個,其中兩個我都詳細介紹過,相信大家不會陌生。

go-spew是一個變量結構體的調試利器,可以打印出變量結構體對應的數據和結構,調試非常方便

gorilla/mux是一個web路由服務,可以很簡單的幫我們構建web服務。

不過目前用gin的比較多,也推薦使用gin https://github.com/gin-gonic/gin。

godotenv是一個讀取配置文章的庫,可以讓我們讀取.env格式的配置文件,比如從配置文件里讀取IP、PORT等。不過目前配置文件還是推薦YAML和TOML,對應的第三方庫是:

gopkg.in/yaml.v21https://github.com/BurntSushi/toml

既然要寫一個區塊鏈,那么肯定的有一個區塊的實體,我們通過golang的struct來實現。

//?Block?represents?each?'item'?in?the?blockchain
type?Block?struct?{
????Index?????int
????Timestamp?string
????BPM???????int
????Hash??????string
????PrevHash??string
}

Block里包含幾個字段:

  • Index 就是Block的順序索引

  • Timestamp是生成Block的時間戳

  • BPM,作者說代表心率,每分鐘心跳數

  • Hash是通過sha256生成的散列值,對整個Block數據的Hash

  • PrevHash 上一個Block的Hash,這樣區塊才能連在一起構成區塊鏈

  • 有了區塊Block了,那么區塊鏈就非常好辦了。

    //?Blockchain?is?a?series?of?validated?Blocks
    var?Blockchain?[]Block

    就是這么簡單,一個Block數組就是一個區塊鏈。區塊鏈的構成關鍵在于Hash和PrevHash,通過他們一個個串聯起來,就是一串Block,也就是區塊鏈。因為相互之間通過Hash和PrevHash進行關聯,所以整個鏈很難被篡改,鏈越長被篡改的成本越大,因為要把整個鏈全部改掉才能完成篡改的目的,這樣的話,其他節點驗證這次篡改肯定是不能通過的。

    既然關鍵點在于Hash,所以我們要先算出來一個Block的數據的Hash,也就是對Block里的字段進行Hash,計算出一個唯一的Hash值。

    //?SHA256?hasing
    func?calculateHash(block?Block)?string?{
    ????record?:=?strconv.Itoa(block.Index)?+?block.Timestamp?+?strconv.Itoa(block.BPM)?+?block.PrevHash
    ????h?:=?sha256.New()
    ????h.Write([]byte(record))
    ????hashed?:=?h.Sum(nil)
    ????return?hex.EncodeToString(hashed)
    }

    sha256是golang內置的sha256的散列標準庫,可以讓我們很容易的生成對應數據的散列值。從源代碼看,是把Block的所有字段進行字符串拼接,然后通過sha256進行散列,散列的數據再通過hex.EncodeToString轉換為16進制的字符串,這樣就得到了我們常見的sha256散列值,類似這樣的字符串8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92。

    Block的散列值被我們計算出來了,Block的Hash和PrevHash這兩個字段搞定了,那么我們現在就可以生成一個區塊了,因為其他幾個字段都是可以自動生成的。

    //?create?a?new?block?using?previous?block's?hash
    func?generateBlock(oldBlock?Block,?BPM?int)?Block?{

    ????var?newBlock?Block

    ????t?:=?time.Now()

    ????newBlock.Index?=?oldBlock.Index?+?1
    ????newBlock.Timestamp?=?t.String()
    ????newBlock.BPM?=?BPM
    ????newBlock.PrevHash?=?oldBlock.Hash
    ????newBlock.Hash?=?calculateHash(newBlock)

    ????return?newBlock
    }

    因為區塊鏈是順序相連的,所以我們在生成一個新的區塊的時候,必須知道上一個區塊,也就是源代碼里的oldBlock。另外一個參數BPM就是我們需要在區塊里存儲的數據信息了,這里作者演示的例子是心率,我們可以換成其他業務中想要的數據。

    Index是上一個區塊的Index+1,保持順序;Timestamp通過time.Now()可以得到;Hash通過calculateHash方法計算出來。這樣我們就生成了一個新的區塊。

    在這里作者并沒有使用POW(工作量證明)這類算法來生成區塊,而是沒有任何條件的,這里主要是為了模擬區塊的生成,演示方便。

    區塊可以生成了,但是生成的區塊是否可信,我們還得對他進行校驗,不能隨便生成一個區塊。在比特幣(BitCoin)中校驗比較復雜,這里作者采用了簡單模擬的方式。

    //?make?sure?block?is?valid?by?checking?index,?and?comparing?the?hash?of?the?previous?block
    func?isBlockValid(newBlock,?oldBlock?Block)?bool?{
    ????if?oldBlock.Index+1?!=?newBlock.Index?{
    ????????return?false
    ????}

    ????if?oldBlock.Hash?!=?newBlock.PrevHash?{
    ????????return?false
    ????}

    ????if?calculateHash(newBlock)?!=?newBlock.Hash?{
    ????????return?false
    ????}

    ????return?true
    }

    簡單的對比Index,Hash是否是正確的,并且重新計算了一遍Hash,防止被篡改。

    到了這里,關于區塊鏈的代碼已經全部完成了,剩下的就是把區塊鏈的生成、查看等包裝成一個Web服務,可以通過API、瀏覽器訪問查看。因為作者這里沒有實現P2P網絡,所以采用的是WEB服務的方式。

    //?create?handlers
    func?makeMuxRouter()?http.Handler?{
    ????muxRouter?:=?mux.NewRouter()
    ????muxRouter.HandleFunc("/",?handleGetBlockchain).Methods("GET")
    ????muxRouter.HandleFunc("/",?handleWriteBlock).Methods("POST")
    ????return?muxRouter
    }

    通過mux定義了兩個Handler,URL都是/,但是對應的Method是不一樣的。

    GET方法通過handleGetBlockchain函數實現,用于獲取區塊鏈的信息。

    func?handleGetBlockchain(w?http.ResponseWriter,?r?*http.Request)?{
    ????bytes,?err?:=?json.MarshalIndent(Blockchain,?"",?"??")
    ????if?err?!=?nil?{
    ????????http.Error(w,?err.Error(),?http.StatusInternalServerError)
    ????????return
    ????}
    ????io.WriteString(w,?string(bytes))
    }

    Blockchain是一個[]Block,handleGetBlockchain函數的作用是把Blockchain格式化為JSON字符串,然后顯示出來。io.WriteString是一個很好用的函數,可以往Writer里寫入字符串。更多參考?Go語言實戰筆記(十九)| Go Writer 和 Reader

    'POST'方法通過handleWriteBlock函數實現,用于模擬區塊的生成。

    func?handleWriteBlock(w?http.ResponseWriter,?r?*http.Request)?{
    ????w.Header().Set("Content-Type",?"application/json")

    ????//使用了一個Mesage結構體,更方便的存儲BPM
    ????var?msg?Message

    ????//接收請求的數據信息,類似{"BPM":60}這樣的格式
    ????decoder?:=?json.NewDecoder(r.Body)
    ????if?err?:=?decoder.Decode(&msg);?err?!=?nil?{
    ????????respondWithJSON(w,?r,?http.StatusBadRequest,?r.Body)
    ????????return
    ????}
    ????defer?r.Body.Close()

    ????//控制并發,生成區塊鏈,并且校驗
    ????mutex.Lock()
    ????prevBlock?:=?Blockchain[len(Blockchain)-1]
    ????newBlock?:=?generateBlock(prevBlock,?msg.BPM)

    ????//校驗區塊鏈
    ????if?isBlockValid(newBlock,?prevBlock)?{
    ????????Blockchain?=?append(Blockchain,?newBlock)
    ????????spew.Dump(Blockchain)
    ????}
    ????mutex.Unlock()

    ????//返回新的區塊信息
    ????respondWithJSON(w,?r,?http.StatusCreated,?newBlock)

    }

    以上代碼我進行了注釋,便于理解。主要是通過POST發送一個{"BPM":60}格式的BODY來添加區塊,如果格式正確,那么就生成區塊進行校驗,合格了就加入到區塊里;如果格式不對,那么返回錯誤信息。

    用于控制并發的鎖可以參考Go語言實戰筆記(十七)| Go 讀寫鎖

    這個方法里有個Message結構體,主要是為了便于操作方便。

    //?Message?takes?incoming?JSON?payload?for?writing?heart?rate
    type?Message?struct?{
    ????BPM?int
    }

    返回的JSON信息,也被抽取成了一個函數respondWithJSON,便于公用。

    func?respondWithJSON(w?http.ResponseWriter,?r?*http.Request,?code?int,?payload?interface{})?{
    ????response,?err?:=?json.MarshalIndent(payload,?"",?"??")
    ????if?err?!=?nil?{
    ????????w.WriteHeader(http.StatusInternalServerError)
    ????????w.Write([]byte("HTTP?500:?Internal?Server?Error"))
    ????????return
    ????}
    ????w.WriteHeader(code)
    ????w.Write(response)
    }

    好了,快完成了,以上Web的Handler已經好了,現在我們要啟動我們的Web服務了。

    //?web?server
    func?run()?error?{
    ????mux?:=?makeMuxRouter()
    ????//從配置文件里讀取監聽的端口
    ????httpPort?:=?os.Getenv("PORT")
    ????log.Println("HTTP?Server?Listening?on?port?:",?httpPort)
    ????s?:=?&http.Server{
    ????????Addr:???????????":"?+?httpPort,
    ????????Handler:????????mux,
    ????????ReadTimeout:????10?*?time.Second,
    ????????WriteTimeout:???10?*?time.Second,
    ????????MaxHeaderBytes:?1?<20,
    ????}

    ????if?err?:=?s.ListenAndServe();?err?!=?nil?{
    ????????return?err
    ????}

    ????return?nil
    }

    和原生的http.Server基本一樣,應該比較好理解。mux其實也是一個Handler,這就是整個Handler處理鏈。現在我們就差一個main主函數來啟動我們整個程序了。

    //控制并發的鎖
    var?mutex?=?&sync.Mutex{}

    func?main()?{
    ????//加載env配置文件
    ????err?:=?godotenv.Load()
    ????if?err?!=?nil?{
    ????????log.Fatal(err)
    ????}

    ????//開啟一個goroutine生成一個創世區塊
    ????go?func()?{
    ????????t?:=?time.Now()
    ????????genesisBlock?:=?Block{}
    ????????genesisBlock?=?Block{0,?t.String(),?0,?calculateHash(genesisBlock),?""}
    ????????spew.Dump(genesisBlock)

    ????????mutex.Lock()
    ????????Blockchain?=?append(Blockchain,?genesisBlock)
    ????????mutex.Unlock()
    ????}()
    ????log.Fatal(run())

    }

    整個main函數并不太復雜,主要就是加載env配置文件,開啟一個go協程生成一個創世區塊并且添加到區塊鏈的第一個位置,然后就是通過run函數啟動Web服務。

    一個區塊鏈都有一個創世區塊,也就是第一個區塊。有了第一個區塊我們才能添加第二個,第三個,第N個區塊。創世區塊因為是第一個區塊,所以它是沒有PrevHash的。

    終于可以運行了,假設我們設置的PORT是8080,現在我們通過go run main.go啟動這個簡易的區塊鏈程序,就可以看到控制臺輸出的創世區塊信息。然后我們通過瀏覽器打開http://localhost:8080也可以看到這個區塊鏈的信息,里面只有一個創世區塊。

    如果我們要新增一個區塊,通過curl或者postman,向http://localhost:8080?發送body格式為{"BPM":60}的POST的信息即可。然后在通過瀏覽器訪問http://localhost:8080查看區塊鏈信息,驗證是否已經添加成功。

    到這里,整個源代碼的分析已經完了,我們看下這個簡易的區塊鏈涉及到多少知識:

  • sha256散列

  • 字節到16進制轉換

  • 并發同步鎖

  • Web服務

  • 配置文件

  • 后向式鏈表

  • 結構體

  • JSON

  • ……

  • 等等,上面的很多知識,我已經在文章中講解或者通過以前些的文章說明,大家可以看一下,詳細了解。


    推薦閱讀

    • go語言的開源區塊鏈代碼都有哪些?歡迎留言補充

    學習交流 Go 語言,掃碼回復「進群」即可

    站長 polarisxu

    自己的原創文章

    不限于 Go 技術

    職場和創業經驗

    Go語言中文網

    每天為你

    分享 Go 知識

    Go愛好者值得關注

    總結

    以上是生活随笔為你收集整理的r语言error in match.fun(fun) :_Go语言200行写区块链源代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。